As a user and as the family tech support I believe a curated App Store provides imense value. A user should be able to install an app with confidence and not fear it messing with their device or their private data. Honestly, this should’ve always been the case. There’s a tendency for us geeks to look at the internet as our playground, and forget about those who don’t work with computers for a living.
But as a developer, the App Store can be pretty hostile. I’ve been dealing with the iOS App Store since the very beginning and the road as been filled with ups and downs. From getting featured, to having to appeal a rejection. From easily getting into the top charts, to forever being in the long tail.
More recently I’ve also been working with the Mac App Store. Since its inception the Mac App Store has lagged behind its iOS counterpart. To this day there’s still no TestFlight or App Store analytics for Mac. And there was a time where you couldn’t even test Push Notifications or CloudKit on production services1.
Shorter App Review Times
App Review times have always been a source of stress for us developers. We never know what to expect. Normally an app would take at least a week to be reviewed. This always seemed way to long to me, but that was the norm and we got used to it.
In 2016, after Phill Schiller took over the App Stores, app review times saw a dramatic improvement. And this applied to both iOS and the Mac.
Regarding the shorter app review time trend… Secrets was no exception, both iOS & OS X versions were approved (and rejected 😏) in <2 days
— Paulo Andrade @pfandrade_ May 12, 2016
But although the tendency for shorter app review times was sustained, the unpredictability was still there… at least on the Mac App Store. In the last year alone there were at least a couple of occasions where Secrets for Mac got stuck in either “Waiting for Review” or “In Review” for at least a week. In both cases, contacting App Review seemed to unblock the issue.
From the outside, it still looked like the Mac App Store was dealt as an afterthought.
Mojave’s All-New Mac App Store
This year Apple made two big announcements at WWDC regarding the Mac App Store.
The first, a revamped UI. Including the awesome editorial stories we’ve become accustomed to on iOS.
And it sure looks great. Certainly a great improvement over what was there before, albeit sometimes it feels more like an iOS app running on the Mac than a native Mac app.
The second, and to me much more meaningful, the return of various apps to the Mac App Store. Namely from developers like Panic and Bare Bones, who were on the App Store but left for one reason or another.
The details of what Apple told these developers to make them come back to the Mac App Store are still scarce, but I took it as sign that Apple wasn’t just putting lipstick on a pig. Or so I hoped.
Reality check
Alas, the reason that triggered this post. After Mojave’s announcement, I quickly started working on Secrets so that it could be available on launch day. I’ve had Dark Mode support done as early as June 14th. I was in pretty good shape, but I could only submit once the Xcode GM was released. And then this happened:
-
Sep 12th - Xcode 10 GM is released.
-
Sep 13th - Tried submitting to the Mac App Store but failed because it refuses to accept binaries with the new
com.apple.security.automation.apple-events
. Since Mojave’s release is still a week and half away, I contact Apple and wait for a response. Surely other people must be facing this issue and there’s still time.Hmm… so we can’t submit apps with the Apple Events entitlement yet?
— Paulo Andrade @pfandrade_ September 13, 2018
With the amount of love/attention Apple Events have been getting from Apple, this is not surprising…#AEpocalypse pic.twitter.com/sy3F8XADbt -
Sep 18th - Still no reply from Apple. Fearing the app wouldn’t be available on launch I decide to disable the Hardened Runtime2 and remove the entitlement. Application gets processed and is now “Waiting For Review”.
-
Sep 24th - App is still waiting for review. Mojave is supposed to be released today. I submit an expedite review request explaining the issue mentioned above. The expedite review request is accepted.
-
Sep 25th - The app changes to “In Review”.
-
Sep 27th - I contact Apple, letting them know an expedited review was accepted but it’s sitting “In Review” for a couple of days. Apple responds stating that they’re escalating the issue.
-
Sep 28th - The app is rejected with a crash on launch, crash logs are attached. I examine the crash log, and I can see the problem stems from my receipt validation. This code has been untouched since I initial wrote it and I don’t recall it ever crashing. I try really hard to understand where the crash is coming from, but without the receipt App Review was using I can’t figure it out. I can either ask for the receipt they were using, wait another day for a response that most likely is going to be to contact DTS or dismiss this as fluke. By this time I already had a few other bug fixes implemented so I opt for the latter and submit a new binary. App is now “Waiting For Review” again. Also, if the app crashes on launch, why would they take 3 days to reject it?!
-
Oct 8th - By this time the app has been waiting for review for 10 days. Mojave has been out for 14 days, and even if the app was approved immediately, I’d be embarassed to anounce it. I contact app review once again… they reply saying they’re escalating the issue again. Note that expedite requests don’t get rescinded after a rejection.
“If your app is rejected during this review, it is not necessary to request another Expedited Review when you resubmit. Once you revise and resubmit your binary (or metadata, in the case of a Metadata Rejection), your app will be automatically returned to the expedite queue.”
Today is October 12, 30 days after my initial submission attempt, Secrets 2.8.0 is still not available on the Mac App Store. Besides knowing my issue was escalated, I have no idea what’s going on, why it’s taking so long or when can I expect it to be reviewed. If anyone at Apple evers reads this, just take a moment and try to put yourself on my shoes.
I refuse to believe this is the best we can do regarding app curation.
Update on October 13th, 2am:
A few hours after posting this, Secrets moved to “In Review”. I have no idea if that was a result of this post or not, but here’s the rest of the story:
- Oct 12th - The app moves to “In Review” and is quickly rejected for the same crash at startup mentioned above. I try my chances and ask for the receipt they’re using and, as expected, I get directed to DTS. That road was going to take way too long, with no guarantees of success, so I try something else. I change my code so I can get a better crash log3. I let the reviewer know what I’m doing and submit again. A few moments later the app is reviewed and rejected again with a new crash log. For reasons too convoluted long to explain here, this crash log is not enough. Myself and the reviewer do this dance another 3 times, and the reviewer is always quick to get back to me (which is something I’ve never seen before). Long story short, the receipt Apple is using during App Review has a nil Original Application Version, which caused a crash in my code to determine whether or not Premium was enabled. More precisely, I needed to compare the original version the user bought to the version I introduced IAPs, which caused an exception to be thrown. These review receipts must’ve changed recently since this code is the same since Secrets 2.0.0. Also note that neither the MAS sandbox or production ever generate receipts with this value set to nil.
This back & forth took about 5 hours, and the app is finally approved at about 1am Oct 13th.
-
There are no Ad Hoc builds for macOS, and only since macOS 10.11 can developers use Push Notifications and CloudKit outside the Mac App Store with their Developer ID. ↩︎
-
The Hardened Runtime is also new on Mojave. That entitlement is required when this is enabled. I’m removing features because I can’t get through App Review. ↩︎
-
An exception was being thrown inside a
dispatch_once
call, which catches the exception itself and callsabort()
. The net effect is that the crash log I receive is from thedispatch_once
call up. ↩︎