Focusmark

Fighting App Store rejection due to reviewer's sandbox

4 min read

Over the past two weekends, I decided to build a new app. Whisper Memos. In short, it’s an iOS app that lets you record a voice memo, converts to text using OpenAI Whisper technology, and sends you an email with the transcription.

Building the UI in SwiftUI was easy. It’s just a basic list view with a few modals for things like settings. The database part was easy too. Simple Firebase setup, with some processing on the server side.

For running Whisper library, there are two options: Deepgram API, currently free, or spinning up a model on banana.dev. The latter should be about $1.5 per hour of recording.

The surprisingly most annoying part was the subscriptions. I decided to use RevenueCat, to make sure there’s very little work for me to do. For implementation, RevenueCat is very easy:

func purchase(package: Package) {
    self.isPurchasing = true

    Purchases.shared.purchase(package: package) { transaction, customerInfo, error, userCancelled in
        self.isSubscribed = customerInfo?.entitlements["amber"]?.isActive ?? false
        self.isPurchasing = false
    }
}

Submitting to the App Store was not that easy. I got stuck on this issue:

We found that your in-app purchase products exhibited one or more bugs which create a poor user experience. Specifically, the subscription content was not unlocked after the purchase. Please review the details and resources below and complete the next steps.

And behold, they sent me a screenshot where the subscriptions don’t work. At first, I tried the strategy of just making a minor change and resubmitting. Sometimes even just a new build fixes the problem.

But this was worse. After going back and forth 3 times, they kept sending me the same problem. Something was not right.

The first thing you’ll want to do in this situation is to log the error somewhere to cloud. I was already using Firebase, so I just wrote the possible error during purchase:

Purchases.shared.purchase(package: package) { transaction, customerInfo, error, userCancelled in
        if let error = error {
            self.errorsStore.create(error: "Error purchasing: (error.description)")
        }
        // ...
}

And sure enough I got error code MISSING_RECEIPT_FILE, “The receipt is missing”:

Error purchasing: Error Domain=RevenueCat.ErrorCode Code=0 "Unknown error."
UserInfo={source_file=RevenueCat/PurchasesOrchestrator.swift:403,
source_function=purchase(sk2Product:promotionalOffer:completion:),
NSUnderlyingError=0x283b7db30 {Error Domain=RevenueCat.ErrorCode Code=9 "The
receipt is missing."
UserInfo={source_function=handlePurchasedTransaction(_:storefront:),
source_file=RevenueCat/PurchasesOrchestrator.swift:687,
readable_error_code=MISSING_RECEIPT_FILE, NSLocalizedDescription=The receipt is
missing.}}, NSLocalizedDescription=Unknown error., readable_error_code=UNKNOWN}

After Googling this, two forum topics looked useful:

I tried resubmitting a bunch of times - that never worked. I tried asking the reviewer to try and log in with another Sandbox account, also no success.

Finally I tried a bit of advice that I saw in one of the forum threads: Delete all your subscriptions, and create new ones, with different IDs.

Miraculously this finally worked and the app got approved. It took about 10 attempts. I was lucky to get a quick review on each attempt.

So go ahead, and try Whisper Memos! I’m just hoping that the next time I submit an update, it won’t be as painful.


Focusmark

I'm Vojtech, a software engineer who keeps looking for ways to be more focused in work and life. I'm building FocusTask and Whisper Memos.