iOS TestFlight with different configuration - ios

Our service endpoints are currently hard coded in our app. We have multiple environments, dev-test, UAT and Live.
We want to leverage TestFlight for our UAT stage, however this would not be the version we can push live, as the service endpoint within the app would need to be swapped for the Live endpoint.
Is there anyway to provide configuration for your app that can be set to one value in TestFlight, and another in Live?

Actually, there seems to be a way to actually differ Testflight from App Store, at least for the moment:
func isTestflight() -> Bool {
guard let receiptURL = Bundle.main.appStoreReceiptURL else {
return false
}
return receiptURL.lastPathComponent == "sandboxReceipt"
}
Source: https://mobile.twitter.com/kraustifer/status/1090773523860058112

Related

Redirect to phone settings like Wifi/bluetooth from within iOS app

I know this is a common question but since there are lots of things changing with each ios update , curious to ask this again.
I have a requirement in iOS app (implemented using ionic framework) , where there must be an option to go to Bluetooth settings of the iphone from within the app so that the user can turn it on/off.
I have read several articles saying that Apple may reject apps trying to access phone settings and it is not advisable to access phone settings through app. Can someone clarify if this still holds true with latest iOS versions and should I never try to do this in future?
You cannot open the Bluetooth settings by using
App-Prefs:root=Bluetooth
The issue is that Apple does not allow us to use non-public APIs anymore and you maybe at risk of getting your developer program cancelled if you try to do so.
All you can do is open the iPhone settings in general by using this code:
extension UIApplication {
static func openAppSettings(completion: #escaping (_ isSuccess: Bool) -> ()) {
guard let url = URL(string: UIApplication.openSettingsURLString) else {
completion(false)
return
}
UIApplication.shared.open(url) { isSuccess in
completion(isSuccess)
}
}
}
Usage:
UIApplication.openAppSettings { isSuccess in
if isSuccess == false {
//Display error
}
}

AppStore.sync() not restoring purchases

On an app that was using the old API for In-App Purchases (StoreKit 1). The app is already published on the App Store. The purchase is non-consumable.
While trying to migrate to StoreKit 2, I'm unable to restore purchases.
Specifically displaying and purchasing products works as expected, but when deleting and reinstalling the app, and then trying to restore purchases I can't do it.
I'm trying to restore them using the new APIs but it doesn't seem to be working.
What I have tried so far:
I'm listening for transaction updates during the whole lifetime of the app, with:
Task.detached {
for await result in Transaction.updates {
if case let .verified(safe) = result {
}
}
}
I have a button that calls this method, but other than prompting to log in again with the Apple ID it doesn't seem to have any effect at all:
try? await AppStore.sync()
This doesn't return any item
for await result in Transaction.currentEntitlements {
if case let .verified(transaction) = result {
}
}
This doesn't return any item
for await result in Transaction.all {
if case let .verified(transaction) = result {
}
}
As mentioned before I'm trying this after purchasing the item and deleting the app. So I'm sure it should be able to restore the purchase.
Am trying this both with a Configuration.storekit file on the simulator, and without it on a real device, in the Sandbox Environment.
Has anyone being able to restore purchases using StoreKit 2?
PD: I already filed a feedback report on Feedback Assistant, but so far the only thing that they have replied is:
Because StoreKit Testing in Xcode is a local environment, and the data is tied to the app, when you delete the app you're also deleting all the transaction data for that app in the Xcode environment. The code snippets provided are correct usage of the API.
So yes, using a Configuration.storekit file won't work on restoring purchases, but if I can't restore them on the Sandbox Environment I'm afraid that this won't work once released, leaving my users totally unable to restore what they have already purchased.
After releasing to the App Store and finally trying the app directly in production I can confirm that it works, but I have to say that it is not ideal to be unable to test this on the sandbox environment.
Also I feel the documentation was not clear enough, at least not for me.
Probably it is clear for other folks, but I was expecting the purchases to be restored automatically and get them on for await result in Transaction.updates, but this didn't work.
What did work was to check Transaction.currentEntitlements, and if the entitlement is not there, then do a sync() and check again.
This is the code that worked for me:
try? await AppStore.sync()
for await result in Transaction.currentEntitlements {
if case let .verified(transaction) = result {
// ...
}
}
Caveats
As mentioned before, this only works on release mode and doesn't work on debug mode without StoreKit Testing, that is without a Configuration.storekit.
If you want to use StoreKit Testing (using a Configuration.storekit) restoring purchases works (with this same approach), but only if you don't delete de app and reinstall again. By deleting the app you loose StoreKit Testing history.
As mentioned by #loremipsum, this can be tested on TestFlight too (for it being release mode).
Verified both iOS and macOS.

Swift: Get Mobile Data permission state

I have an application which I was testing on iOS 10.3 since few days and it was working fine. I recently tested it on an iOS 12 device and it was not working as expected. The application was not able to connect to my server.
Upon investigation, I found that the "Mobile Data" switch was turned off for my application in Settings -> AppName. After turning it on, it started working perfectly.
So, given this scenario, is there a way I can determine the status of this switch from my code? If I can know the status and if it's off, I can redirect the user to the application setting using:
let urlObj = NSURL.init(string:UIApplicationOpenSettingsURLString)
if #available(iOS 10.0, *) {
UIApplication.shared.open(urlObj as! URL, options: [ : ], completionHandler: { Success in
})
} else {
let success = UIApplication.shared.openURL(url as URL)
print("Open \(url): \(success)")
}
P.S: I am not looking for a solution using Reachability as it is not completely reliable.
There is no way to check this setting in the latest iOS release. You have two options to deal with this: first is you check if device is not connected to WiFi, and also not in airplane mode. If server can not be reached it’s safe to assume that data is disabled.
Second option is just to present user errors saying the application is unable to reach the server and to change UI/notice in application accordingly to deal with any scenario where a network connection can not be reached.
CTCellularData is a potential option however as of iOS 11 has some down falls of how often the state is checked.

In App Purchase working fine in testing mode but not working in app review

I integrated in app purchase in my app and i tested it with created test account. It was working fine but when i submitted it to app store for live in app review my app got rejected due to:
Guideline 2.1 - Performance - App Completeness
When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production,” you should validate against the test environment instead.
From WWDC 2013 Session 308 at 44:00:
"The app reviewers are actually testing your production signed, ready-to-go-into-the-store binary, but against the test environment. So your production signed app will see test environment receipts".
What this means is that if you're using a server to validate your receipt, make sure to validate the receipt against the production server first, and if that validation fails, then validate the receipt against the sandbox server.
This is what is instructed to do in the rejection reason you received:
"The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production,” you should validate against the test environment instead."
Please also see the following answers to similar questions:
https://stackoverflow.com/a/49398571/5948155
https://stackoverflow.com/a/21515982/5948155
Actually I am using this function to purchase:
func purchase(_ purchase: RegisteredPurchase) {
self.view.showLoad()
// NetworkActivityIndicatorManager.NetworkOperationStarted()
SwiftyStoreKit.purchaseProduct(bundleId + "." + purchase.rawValue, atomically: true) { result in
// NetworkActivityIndicatorManager.NetworkOperationFinished()
self.view.hideLoad()
print(result)
if case .success(let purchase) = result {
self.refrenceNumber = purchase.transaction.transactionIdentifier!
// print(purchase.transaction.transactionDate)
self.sendRefrence()
if purchase.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
}else{
if let alert = self.alertForPurchaseResult(result) {
self.showAlert(alert)
}
}
}
}
It working fine in testing environment but in review its response is :
paymentInvalid
How can i change environment for purchase.
Actually I understand the issue, but when I am purchasing in app purchase product. I am not validating receipt. I am direct purchasing and after calling purchase function as i updated above it gives error .paymentinvalid. where i have to validate or change or where i have to give sandbox or production url to purchase or check. Because my function is not checking any thing directly gives error.

Does this code pass the app store?

I need to know if user has certain apps on his iphone device
I have this code
BOOL isInstalled = [[LSApplicationWorkspace defaultWorkspace] applicationIsInstalled:#"com.app.identifier"];
if (isInstalled) {
// app is installed }
else {
// app is not installed
}
which in theory does the job
the question is in practice, does it pass the app store?
can i use the "LSApplicationWorkspace" class ?
No.
All applications referencing private APIs and even undocumented APIs are not allowed.

Resources