Posts

Post not yet marked as solved
10 Replies
2.9k Views
I have a bunch of questions about universal purchases between iOS and Mac. I'm hoping I can get answers to these soon—or at least before March—but so far I don't think Apple has shared enough information to answer these definitively. I thought I'd ask them here anyway in case anyone can help.Will universal purchase have any OS requirements?As far as I can tell most of it will happen server side—all the appropriate info should just appear in the receipt like it normally would, and there aren't any changes to the receipt format or StoreKit. The only reason I can think of for an OS requirement would be if the App Store needs to be a particular version just to be able to download the app in the first place. Regardless, this seems like an incredibly important question to have answered before I can decide whether supporting universal purchase is an option.What does that mean for existing apps?Apple has made it clear the apps will need to have the same bundle ID. In my case I have an iOS app with the bundle ID com.company.AppName and a Mac app with the bundle ID com.company.mac.AppName. If I want to use universal purchase in the future, I'll change the bundle ID of the Mac app. Does that mean I'll effectively have two completely separate Mac apps? I'm assuming that my Mac users will have to re-download the app—it won't update automatically due to the new bundle ID. I'm also assuming that the receipts will be separate—the app with the new bundle ID wouldn't show any record of purchases tied to the old bundle ID. Is that correct? From what I gather this is how it has worked with iOS/tvOS universal purchases, so I think my assumptions are correct.Are there any concerns related to different App ID Prefixes?I doubt this will be a concern for most developers, but my iOS app is old enough that it has a unique App ID Prefix that is not the same as the Team ID. When I released my Mac app, the only option was to use my Team ID. Is there anything I need to watch out for if I switch the Mac app to use the same bundle ID as my iOS app? From what I can tell, using the same bundle ID means my Mac app will now have the same App ID Prefix as my iOS app. This has an effect on keychain access groups of course. Do I need to look out for anything else?
Posted Last updated
.
Post not yet marked as solved
3 Replies
5.6k Views
While iOS 14 and macOS Big Sur 11 are in public beta, I'd like to add some conditional code to work around significant issues. I'm still building my app with the current SDKs because I plan to release updates before the fall, when the new OS versions are released. Some time back, the recommended way to handle this in Objective-C was to check if a class existed before using it: if (NSClassFromString(@"UINewClassName")) Or you could check if a class had been added to an existing method: if ([object respondsToSelector:@selector(newMethodName:)]) { Then at some point we got #available in Swift and @available in Objective-C, which is now the recommended way to handle this: if #available(iOS 14, *) if (@available(iOS 14.0, *)) This works great if you are building your app with the iOS 14 SDK, but from what I can tell the condition is simply ignored if you are building with an older SDK, at least in Swift. I could go back the old NSClassFromString or respondsToSelector: approach, but I would prefer to handle this in Swift if it's possible. I also have some cases where there's not really a new class or method to check for—it's just new behavior I want to accommodate. What's the best way to handle these cases?
Posted Last updated
.
Post not yet marked as solved
1 Replies
2.4k Views
The documentation for TimelineProvider suggests using Background App Refresh to keep widget data up to date - https://developer.apple.com/documentation/widgetkit/timelineprovider. This approach would work well for my app. However in my testing so far, it doesn't seem like viewing a widget on the home screen has the same effect on background app refresh that actually opening the app would. That seems like a problem—if the information is right there on the home screen, the app is going to be opened far less often. Has anyone else experimented with this, or does anyone know what the expected behavior is here?
Posted Last updated
.
Post not yet marked as solved
1 Replies
827 Views
I’m trying to figure out the best way to handle some things in my widget. For example, I have a database that all of my widgets get their data from. Ideally I’d like to load and initialize my database once, then query the same database instance from each timeline provider. Is that possible? So far I've tried approaching this in a couple of different ways: I can’t seem to find any shared object in the widget extension. For example, in an app I can call UIApplication.shared from anywhere, or in a watchOS app there’s WKExtension.shared(). From there I can access my custom delegate, which is useful in certain situations. Does a WidgetKit extension have something like this that I'm missing? I thought I might be able to create an instance of the database in the WidgetBundle and then pass it through to each timeline provider. But it seems that a Widget can’t have initialization parameters, so I don’t see how I could pass that object in. I’m guessing this is by design—it seems like Apple wants widget extensions to be super lightweight and do work in the parent app whenever possible. But I’d like to be sure before I make some decisions on how to handle everything. (Especially since I’m also having trouble with doing the work through background app refresh - https://developer.apple.com/forums/thread/659373.)
Posted Last updated
.
Post not yet marked as solved
1 Replies
2.4k Views
I'm working on an app that uses subscriptions. It is relatively simple. There is a choice of a monthly or yearly subscription. They are part of the same subscription group, so only one subscription can be active at a time. My app sends the receipt data to my server, which uses the verifyReceipt endpoint to validate the receipt. Aside from checking that the receipt is valid, I’m mainly just interested in one thing: what is the expiration date for the most recent purchase or renewal? So far I’ve mainly worked in the sandbox. I have validated production receipts as well, but those do not contain in-app purchases or subscriptions since I have not yet released an app that includes these. As far as finding the latest purchase, I basically just check the first transaction in latest_receipt_info, which in my testing only ever contains one item. Overall things seem to be working well. While a subscription is active, my app shows the upcoming expiration or renewal date, and I've never noticed any problems with that. After the subscription expires, the app shows a notice explaining when it expired. I've recently noticed this date is not accurate. When this happens, latest_receipt_info contains a single transaction that is not the most recent transaction. I’ve never seen this problem while a subscription is active. Here are the questions I have right now: Will latest_receipt_info actually contain multiple items for an app with a single subscription group? The sandbox only ever returns a one-item array for me. When a subscription is active, it seems to be accurate. When a subscription has expired, it seems to be an effectively random purchase—it’s different on every refresh. If there may be multiple items in latest_receipt_info, are they in chronological order or is it important to check all of them to find the latest expiration date? The documentation for in_app - https://developer.apple.com/documentation/appstorereceipts/responsebody/receipt/in_app makes it clear that the items will not be in chronological order, but the documentation for latest_receipt_info - https://developer.apple.com/documentation/appstorereceipts/responsebody/latest_receipt_info simply says “an array that contains all in-app purchase transactions” which from what I understand is not even accurate. (Elsewhere I’ve heard it’s the 100 most recent transactions, but again I’ve only ever seen one transaction here.) In the sandbox I’ve found that the in_app array contains many transactions that are not included in latest_receipt_info. In some cases, the most recent transaction only appears in the in_app array. I’ve heard from others that latest_receipt_info sometimes contains recent transactions that are not in the in_app array. Do I need to check through all items in both arrays to find the latest expiration date? Or is it safe to trust that latest_receipt_info will contain the correct information in production? Previously my understanding was that I didn't have to worry about the in_app array for my app, but in the sandbox it seems to be the only way to get the latest expiration date. For my first two questions it seems safest to assume that latest_receipt_info may contain multiple items that may not be in chronological order. That's an easy fix. But unless I'm looking at something wrong, it won't address the problem I've run into. So that last bullet point is my biggest question right now. Bonus questions: In the sandbox, server-to-server notifications don’t ever seem to include grace_period_expires_date_ms. I’m checking for that value in unified_receipt.pending_renewal_info[0]. grace_period_expires_date_ms. Is the correct place to look? For an app with a single subscription group, would the pending_renewal_info array ever contain more than one item? I think I'm relatively safe assuming it would not. It's unclear how I would find the most relevant item if I'm wrong.
Posted Last updated
.
Post not yet marked as solved
3 Replies
654 Views
I'm transitioning an app from paid upfront to free to download with In App Purchases. Those who previously purchased the app will have features unlocked that are not available to those who downloaded the app for free. Since the receipt does not include the purchase price, the only way to detect who paid for the app is based on the version number.Unfortuantely App Store Connect does not have any way to tie a price change to a version update, so I'm trying to figure out the safest way to coordinate this. Here's my tentative plan:Submit the new version for review and set it to "Manually release this version"Once the new version is approved, remove the app from saleRelease the new versionChange the price and make the app available againI'm open to any recommendations on how to ensure this works as well as possible. I have no idea how any of this will work—for example can I actually start the process of releasing an update while the app is removed from sale? If so, is it worth waiting a bit afterward, for that change to propagate, before I make the app available at the new price?
Posted Last updated
.
Post not yet marked as solved
2 Replies
3.0k Views
I'm working on an app that requires a subscription. Subscription times are drastically shortened in the sandbox, including during TestFlight. This is fine for internal testing, and perhaps during initial TestFlight testing, but for the most part I want my users to be testing the app, not the subscription process. As it is, even a yearly subscription only lasts an hour, and will only auto-renew six times. It's absurd to expect my testers to renew their subscription every day, let alone multiple times a day.Is there a recommended way to deal with this? It seems like a major oversight. The only good solutions I can think of are:Offer a one-time purchase to unlock the app for life. I don’t want to make this available on the App Store, but I guess I could create it and just never publish it…? Would that work?Add a completely custom way to handle this. For example, if the app has an account system users can log into, then accounts could be flagged a being beta testers, and that info could be communicated back to the app. This is a possibility for this app, but I’d like people to be able to test the app without signing in if they prefer. If I require my testers to all use the app in some specific way, it's not going to get tested very well.Check the receipt URL to determine whether the app is using the sandbox. This seems the most prone to failure since it is not documented and could change. (It also seems the least secure, for example if someone wanted to crack the app on a jailbroken device.)Another big downside is that either my beta testers spend far too much time testing my subscription code, or they have some way to fully unlock the app and at that point they never test my subscription code at all. Both of these are awful.Does anyone have a recommendation?
Posted Last updated
.
Post marked as solved
6 Replies
4k Views
After updating to Xcode 10.2 I'm getting a bunch of warnings like this:Showing All Messages:-1: ignoring build task 'ConstructedTask(forTarget: SoupKit, 3 inputs, 1 outputs, ruleInfo: ["CopyStringsFile", "/Users/username/Library/Developer/Xcode/DerivedData/SoupChef-rojbzdehurmlgakeaipmwucmevgx/Build/Products/Debug-iphoneos/SoupKit.framework/en.lproj/Intents.strings", "/Users/username/Downloads/SoupChefAcceleratingAppInteractionsWithShortcuts/Shared/en.lproj/Intents.strings"])' which produces files already produced by another task:/Users/username/Library/Developer/Xcode/DerivedData/SoupChef-rojbzdehurmlgakeaipmwucmevgx/Build/Products/Debug-iphoneos/SoupKit.framework/en.lproj/Intents.stringsThis warning represents an extremely serious project misconfiguration and will cause some build tasks in your project to be skipped entirely, leading to other build failures or missing files in the build directory. (in target 'SoupKit')Now, I don't think this is actually a major issue, but the last line sure makes it seems like it's a very bad idea to just ignore this. After some experimenting I've determined this issue is very easy to reproduce:1. Add an Intents.intentdefinition file to a project2. Add any localization to that file3. Set the deployment target to iOS 10.3 or earlierIf I set the deployment target to iOS 11, or remove all localizations from the Intents file, the warning goes away. Neither of these is an acceptable solution, but again, I also don't like ignoring the warning.At this point, I'm trying to figure out the answers to any of these questions:1. Whatever is going on here, my assumption is that the same thing was happening in Xcode 10.1, it just wasn't telling me about it. Meaning that going back to 10.1 until this is fixed would be no better than ignoring the error. Is that likely to be true?2. The errors are only about strings files, and it seems like it's just replacing one copy of the file with the same file. Am I safe to ignore it, especially since I can just manually inspect the build to verify the files are there?3. Is there anything else I might be able to do to solve the issue?I've file rdar://49274887 on the this problem:https://openradar.appspot.com/radar?id=5000918072295424
Posted Last updated
.