How can you get Original Application Version in Production?
We recently transitioned a purchasable app to the "freemium" model. We are using Bundle.main.appStoreReceiptURL to pull the receipt and then check the "original_application_version" to see if a user downloaded an older paid version from the App Store, or if they downloaded a newer free version with a non-consumable in app purchase to upgrade to the full version.
This works perfectly when in testing Sandbox, but in Production, older versions of the app are not properly verifying that they were downloaded prior to the freemium version.
The following code is called with productionStoreURL and the receipt obtained from Bundle.main.appStoreReceiptURL:
private let productionStoreURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
private let sandboxStoreURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")
private func verifyIfPurchasedBeforeFreemium(_ storeURL: URL, _ receipt: Data) {
do {
let requestContents:Dictionary = ["receipt-data": receipt.base64EncodedString()]
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: )
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
URLSession.shared.dataTask(with: storeRequest) { (data, response, error) in
DispatchQueue.main.async {
if data != nil {
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: ) as! [String: Any?]
if let statusCode = jsonResponse["status"] as? Int {
if statusCode == 21007 {
print("Switching to test against sandbox")
self.verifyIfPurchasedBeforeFreemium(self.sandboxStoreURL!, receipt)
}
}
if let receiptResponse = jsonResponse["receipt"] as? [String: Any?], let originalVersion = receiptResponse["original_application_version"] as? String {
if self.isPaidVersionNumber(originalVersion) {
// Update to full paid version of app
UserDefaults.standard.set(true, forKey: upgradeKeys.isUpgraded)
NotificationCenter.default.post(name: .UpgradedVersionNotification, object: nil)
}
}
} catch {
print("Error: " + error.localizedDescription)
}
}
}
}.resume()
} catch {
print("Error: " + error.localizedDescription)
}
}
private func isPaidVersionNumber(_ originalVersion: String) -> Bool {
let pattern:String = "^\d+\.\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: )
let results = regex.matches(in: originalVersion, options: , range: NSMakeRange(0, originalVersion.count))
let original = results.map {
Double(originalVersion[Range($0.range, in: originalVersion)!])
}
if original.count > 0, original[0]! < firstFreemiumVersion {
print("App purchased prior to Freemium model")
return true
}
} catch {
print("Paid Version RegEx Error.")
}
return false
}
The first freemium version is 3.2, which is our current build. All previous builds were 3.1.6 or earlier.
The Production URL shouldn't be the issue, or else it wouldn't kick back the 21007 status code to trigger the Sandbox validation for us. However, troubleshooting this is particularly tricky since we can't test against the Apple's Production URL itself.
Does anyone have any insight as to why this would work in Sandbox but not Production?
ios swift receipt-validation
add a comment |
We recently transitioned a purchasable app to the "freemium" model. We are using Bundle.main.appStoreReceiptURL to pull the receipt and then check the "original_application_version" to see if a user downloaded an older paid version from the App Store, or if they downloaded a newer free version with a non-consumable in app purchase to upgrade to the full version.
This works perfectly when in testing Sandbox, but in Production, older versions of the app are not properly verifying that they were downloaded prior to the freemium version.
The following code is called with productionStoreURL and the receipt obtained from Bundle.main.appStoreReceiptURL:
private let productionStoreURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
private let sandboxStoreURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")
private func verifyIfPurchasedBeforeFreemium(_ storeURL: URL, _ receipt: Data) {
do {
let requestContents:Dictionary = ["receipt-data": receipt.base64EncodedString()]
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: )
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
URLSession.shared.dataTask(with: storeRequest) { (data, response, error) in
DispatchQueue.main.async {
if data != nil {
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: ) as! [String: Any?]
if let statusCode = jsonResponse["status"] as? Int {
if statusCode == 21007 {
print("Switching to test against sandbox")
self.verifyIfPurchasedBeforeFreemium(self.sandboxStoreURL!, receipt)
}
}
if let receiptResponse = jsonResponse["receipt"] as? [String: Any?], let originalVersion = receiptResponse["original_application_version"] as? String {
if self.isPaidVersionNumber(originalVersion) {
// Update to full paid version of app
UserDefaults.standard.set(true, forKey: upgradeKeys.isUpgraded)
NotificationCenter.default.post(name: .UpgradedVersionNotification, object: nil)
}
}
} catch {
print("Error: " + error.localizedDescription)
}
}
}
}.resume()
} catch {
print("Error: " + error.localizedDescription)
}
}
private func isPaidVersionNumber(_ originalVersion: String) -> Bool {
let pattern:String = "^\d+\.\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: )
let results = regex.matches(in: originalVersion, options: , range: NSMakeRange(0, originalVersion.count))
let original = results.map {
Double(originalVersion[Range($0.range, in: originalVersion)!])
}
if original.count > 0, original[0]! < firstFreemiumVersion {
print("App purchased prior to Freemium model")
return true
}
} catch {
print("Paid Version RegEx Error.")
}
return false
}
The first freemium version is 3.2, which is our current build. All previous builds were 3.1.6 or earlier.
The Production URL shouldn't be the issue, or else it wouldn't kick back the 21007 status code to trigger the Sandbox validation for us. However, troubleshooting this is particularly tricky since we can't test against the Apple's Production URL itself.
Does anyone have any insight as to why this would work in Sandbox but not Production?
ios swift receipt-validation
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use ofUserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.
– Jamie Edge
Dec 31 '18 at 20:45
@JamieEdge Thanks for the tip! I will definitely change this. DoesCoreData
share the same risks asUserDefaults
? It seems excessive to validate the receipt every time the user opens.
– Brandogs
Dec 31 '18 at 21:10
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).
– Jamie Edge
Dec 31 '18 at 21:27
add a comment |
We recently transitioned a purchasable app to the "freemium" model. We are using Bundle.main.appStoreReceiptURL to pull the receipt and then check the "original_application_version" to see if a user downloaded an older paid version from the App Store, or if they downloaded a newer free version with a non-consumable in app purchase to upgrade to the full version.
This works perfectly when in testing Sandbox, but in Production, older versions of the app are not properly verifying that they were downloaded prior to the freemium version.
The following code is called with productionStoreURL and the receipt obtained from Bundle.main.appStoreReceiptURL:
private let productionStoreURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
private let sandboxStoreURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")
private func verifyIfPurchasedBeforeFreemium(_ storeURL: URL, _ receipt: Data) {
do {
let requestContents:Dictionary = ["receipt-data": receipt.base64EncodedString()]
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: )
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
URLSession.shared.dataTask(with: storeRequest) { (data, response, error) in
DispatchQueue.main.async {
if data != nil {
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: ) as! [String: Any?]
if let statusCode = jsonResponse["status"] as? Int {
if statusCode == 21007 {
print("Switching to test against sandbox")
self.verifyIfPurchasedBeforeFreemium(self.sandboxStoreURL!, receipt)
}
}
if let receiptResponse = jsonResponse["receipt"] as? [String: Any?], let originalVersion = receiptResponse["original_application_version"] as? String {
if self.isPaidVersionNumber(originalVersion) {
// Update to full paid version of app
UserDefaults.standard.set(true, forKey: upgradeKeys.isUpgraded)
NotificationCenter.default.post(name: .UpgradedVersionNotification, object: nil)
}
}
} catch {
print("Error: " + error.localizedDescription)
}
}
}
}.resume()
} catch {
print("Error: " + error.localizedDescription)
}
}
private func isPaidVersionNumber(_ originalVersion: String) -> Bool {
let pattern:String = "^\d+\.\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: )
let results = regex.matches(in: originalVersion, options: , range: NSMakeRange(0, originalVersion.count))
let original = results.map {
Double(originalVersion[Range($0.range, in: originalVersion)!])
}
if original.count > 0, original[0]! < firstFreemiumVersion {
print("App purchased prior to Freemium model")
return true
}
} catch {
print("Paid Version RegEx Error.")
}
return false
}
The first freemium version is 3.2, which is our current build. All previous builds were 3.1.6 or earlier.
The Production URL shouldn't be the issue, or else it wouldn't kick back the 21007 status code to trigger the Sandbox validation for us. However, troubleshooting this is particularly tricky since we can't test against the Apple's Production URL itself.
Does anyone have any insight as to why this would work in Sandbox but not Production?
ios swift receipt-validation
We recently transitioned a purchasable app to the "freemium" model. We are using Bundle.main.appStoreReceiptURL to pull the receipt and then check the "original_application_version" to see if a user downloaded an older paid version from the App Store, or if they downloaded a newer free version with a non-consumable in app purchase to upgrade to the full version.
This works perfectly when in testing Sandbox, but in Production, older versions of the app are not properly verifying that they were downloaded prior to the freemium version.
The following code is called with productionStoreURL and the receipt obtained from Bundle.main.appStoreReceiptURL:
private let productionStoreURL = URL(string: "https://buy.itunes.apple.com/verifyReceipt")
private let sandboxStoreURL = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")
private func verifyIfPurchasedBeforeFreemium(_ storeURL: URL, _ receipt: Data) {
do {
let requestContents:Dictionary = ["receipt-data": receipt.base64EncodedString()]
let requestData = try JSONSerialization.data(withJSONObject: requestContents, options: )
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
URLSession.shared.dataTask(with: storeRequest) { (data, response, error) in
DispatchQueue.main.async {
if data != nil {
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: ) as! [String: Any?]
if let statusCode = jsonResponse["status"] as? Int {
if statusCode == 21007 {
print("Switching to test against sandbox")
self.verifyIfPurchasedBeforeFreemium(self.sandboxStoreURL!, receipt)
}
}
if let receiptResponse = jsonResponse["receipt"] as? [String: Any?], let originalVersion = receiptResponse["original_application_version"] as? String {
if self.isPaidVersionNumber(originalVersion) {
// Update to full paid version of app
UserDefaults.standard.set(true, forKey: upgradeKeys.isUpgraded)
NotificationCenter.default.post(name: .UpgradedVersionNotification, object: nil)
}
}
} catch {
print("Error: " + error.localizedDescription)
}
}
}
}.resume()
} catch {
print("Error: " + error.localizedDescription)
}
}
private func isPaidVersionNumber(_ originalVersion: String) -> Bool {
let pattern:String = "^\d+\.\d+"
do {
let regex = try NSRegularExpression(pattern: pattern, options: )
let results = regex.matches(in: originalVersion, options: , range: NSMakeRange(0, originalVersion.count))
let original = results.map {
Double(originalVersion[Range($0.range, in: originalVersion)!])
}
if original.count > 0, original[0]! < firstFreemiumVersion {
print("App purchased prior to Freemium model")
return true
}
} catch {
print("Paid Version RegEx Error.")
}
return false
}
The first freemium version is 3.2, which is our current build. All previous builds were 3.1.6 or earlier.
The Production URL shouldn't be the issue, or else it wouldn't kick back the 21007 status code to trigger the Sandbox validation for us. However, troubleshooting this is particularly tricky since we can't test against the Apple's Production URL itself.
Does anyone have any insight as to why this would work in Sandbox but not Production?
ios swift receipt-validation
ios swift receipt-validation
edited Dec 31 '18 at 20:33
Brandogs
asked Dec 31 '18 at 18:56
BrandogsBrandogs
228
228
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use ofUserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.
– Jamie Edge
Dec 31 '18 at 20:45
@JamieEdge Thanks for the tip! I will definitely change this. DoesCoreData
share the same risks asUserDefaults
? It seems excessive to validate the receipt every time the user opens.
– Brandogs
Dec 31 '18 at 21:10
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).
– Jamie Edge
Dec 31 '18 at 21:27
add a comment |
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use ofUserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.
– Jamie Edge
Dec 31 '18 at 20:45
@JamieEdge Thanks for the tip! I will definitely change this. DoesCoreData
share the same risks asUserDefaults
? It seems excessive to validate the receipt every time the user opens.
– Brandogs
Dec 31 '18 at 21:10
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).
– Jamie Edge
Dec 31 '18 at 21:27
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use of
UserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.– Jamie Edge
Dec 31 '18 at 20:45
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use of
UserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.– Jamie Edge
Dec 31 '18 at 20:45
@JamieEdge Thanks for the tip! I will definitely change this. Does
CoreData
share the same risks as UserDefaults
? It seems excessive to validate the receipt every time the user opens.– Brandogs
Dec 31 '18 at 21:10
@JamieEdge Thanks for the tip! I will definitely change this. Does
CoreData
share the same risks as UserDefaults
? It seems excessive to validate the receipt every time the user opens.– Brandogs
Dec 31 '18 at 21:10
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (
UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).– Jamie Edge
Dec 31 '18 at 21:27
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (
UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).– Jamie Edge
Dec 31 '18 at 21:27
add a comment |
1 Answer
1
active
oldest
votes
It looks like it was not a problem with obtaining the receipt at all.
Some of the older values for original_application_version
were not formatted correctly, preventing us from obtaining the app version to compare against.
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53990592%2fhow-can-you-get-original-application-version-in-production%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
It looks like it was not a problem with obtaining the receipt at all.
Some of the older values for original_application_version
were not formatted correctly, preventing us from obtaining the app version to compare against.
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
add a comment |
It looks like it was not a problem with obtaining the receipt at all.
Some of the older values for original_application_version
were not formatted correctly, preventing us from obtaining the app version to compare against.
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
add a comment |
It looks like it was not a problem with obtaining the receipt at all.
Some of the older values for original_application_version
were not formatted correctly, preventing us from obtaining the app version to compare against.
It looks like it was not a problem with obtaining the receipt at all.
Some of the older values for original_application_version
were not formatted correctly, preventing us from obtaining the app version to compare against.
answered Jan 5 at 19:19
BrandogsBrandogs
228
228
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
add a comment |
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
Can you tell something about how they were formatted? I am now checking original_application_version in a production app by detecting if the string contains dots (full stops) but not a single user is getting the correct result.
– Mr. Zystem
yesterday
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
There is no guarantee about how it will be formatted since original_application_version uses the Build field, which developers can set however they want. However, you can check what these values have been in the past by logging into App Store Connect, opening your app, and then opening the Activity tab. Expand each version and look at the values listed in the Build column. Those are the values that are being returned for the original_application_version for that given build.
– Brandogs
11 hours ago
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53990592%2fhow-can-you-get-original-application-version-in-production%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Not an answer to the specific problem that you're asking about, but I'd strongly recommend reconsidering the use of
UserDefaults
to track if paid features should be made available. Tools exist that made it easy to modify user defaults values, even on non-jailbroken devices. This article details how user defaults can be exploited.– Jamie Edge
Dec 31 '18 at 20:45
@JamieEdge Thanks for the tip! I will definitely change this. Does
CoreData
share the same risks asUserDefaults
? It seems excessive to validate the receipt every time the user opens.– Brandogs
Dec 31 '18 at 21:10
Yeah, storing data on the filesystem (such as the underlying SQLite database which would be used with Core Data) will almost always carry this sort of risk, as values are stored within the application's sandbox directory which can be viewed and modified (
UserDefaults
uses a plist file within the sandbox). The keychain is more difficult to modify on a non-jailbroken device - values might be extractable from the a backup, however I'm not aware of any simple way to modify the values (however it very much may be possible).– Jamie Edge
Dec 31 '18 at 21:27