How to detect an iOS App installed or upgraded? [duplicate] - ios

This question already has answers here:
Check if my IOS application is updated
(8 answers)
Closed 5 years ago.
I am developing an application and i need to know whether user installed the app for the first time or upgraded it from the App Store.
How can i detect whether app is installed for the first time or upgraded or re-installed?
Thanks for your answers in advance.

You can differentiate between the first start after installing the App, the first start after an update and other starts quite easily via saving the latest known version to standardUserDefaults. But as far as I know it is not possible do detect a re-install of the App as all App-related data are also removed when the App is deleted from the device.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString* currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"];
NSString* versionOfLastRun = [[NSUserDefaults standardUserDefaults] objectForKey:#"VersionOfLastRun"];
if (versionOfLastRun == nil) {
// First start after installing the app
} else if (![versionOfLastRun isEqual:currentVersion]) {
// App was updated since last run
} else {
// nothing changed
}
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:#"VersionOfLastRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

Checkout Swift 3.0 version of code.
Note: Use CFBundleShortVersionString, for checking actual App version checking.
func checkAppUpgrade() {
let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
let versionOfLastRun = UserDefaults.standard.object(forKey: "VersionOfLastRun") as? String
if versionOfLastRun == nil {
// First start after installing the app
} else if versionOfLastRun != currentVersion {
// App was updated since last run
} else {
// nothing changed
}
UserDefaults.standard.set(currentVersion, forKey: "VersionOfLastRun")
UserDefaults.standard.synchronize()
}

For Swift 3
let currentVersion : String = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
let versionOfLastRun: String? = UserDefaults.standard.object(forKey: "VersionOfLastRun") as? String
if versionOfLastRun == nil {
// First start after installing the app
} else if !(versionOfLastRun?.isEqual(currentVersion))! {
// App is updated
}
UserDefaults.standard.set(currentVersion, forKey: "VersionOfLastRun")
UserDefaults.standard.synchronize()

Just for note:
To obtain localized value of any key you should use CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), "CFBundleShortVersionString" as CFString)

Please store a version in NSUserDefaults (per #Nero's answer) for checking (possible) fresh installs and subsequent updates.
For checking reinstalls (in the case where stored version == nil), exploit iOS 11's introduction of DeviceCheck API which exposes two bits of device specific data which can be set and retrieved by the app, but maintained by Apple and persisted across an uninstall/reinstalls.

Related

uuid changes everytime in iOS [duplicate]

How can I get a device's unique ID in Swift?
I need an ID to use in the database and as the API-key for my web service in my social app. Something to keep track of this devices daily use and limit its queries to the database.
You can use this (Swift 3):
UIDevice.current.identifierForVendor!.uuidString
For older versions:
UIDevice.currentDevice().identifierForVendor
or if you want a string:
UIDevice.currentDevice().identifierForVendor!.UUIDString
There is no longer a way to uniquely identify a device after the user uninstalled the app(s). The documentation says:
The value in this property remains the same while the app (or another app from the same vendor) is installed on the iOS device. The value changes when the user deletes all of that vendor’s apps from the device and subsequently reinstalls one or more of them.
You may also want to read this article by Mattt Thompson for more details:
http://nshipster.com/uuid-udid-unique-identifier/
Update for Swift 4.1, you will need to use:
UIDevice.current.identifierForVendor?.uuidString
You can use devicecheck (in Swift 4)
Apple documentation
func sendEphemeralToken() {
//check if DCDevice is available (iOS 11)
//get the **ephemeral** token
DCDevice.current.generateToken {
(data, error) in
guard let data = data else {
return
}
//send **ephemeral** token to server to
let token = data.base64EncodedString()
//Alamofire.request("https://myServer/deviceToken" ...
}
}
Typical usage:
Typically, you use the DeviceCheck APIs to ensure that a new user has not already redeemed an offer under a different user name on the same device.
Server action needs:
See WWDC 2017 — Session 702 (24:06)
more from Santosh Botre article - Unique Identifier for the iOS Devices
Your associated server combines this token with an authentication key that you receive from Apple and uses the result to request access to the per-device bits.
For Swift 3.X Latest Working Code, Easily usage;
let deviceID = UIDevice.current.identifierForVendor!.uuidString
print(deviceID)
You can use identifierForVendor public property present in UIDevice class
let UUIDValue = UIDevice.currentDevice().identifierForVendor!.UUIDString
print("UUID: \(UUIDValue)")
EDIT
Swift 3:
UIDevice.current.identifierForVendor!.uuidString
END EDIT
In swift 4,5
You can get UUID using below code.
print(UIDevice.current.identifierForVendor!.uuidString)
Output
Apart from that you can get multiple properties from connected Device.
UIDevice.current.name // e.g. "My iPhone"
UIDevice.current.model // e.g. #"iPhone", #"iPod touch"
UIDevice.current.localizedModel // localized version of model
UIDevice.current.systemName // e.g. #"iOS"
UIDevice.current.systemVersion // e.g. #"15.5"
Swift 2.2
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.objectForKey("ApplicationIdentifier") == nil {
let UUID = NSUUID().UUIDString
userDefaults.setObject(UUID, forKey: "ApplicationIdentifier")
userDefaults.synchronize()
}
return true
}
//Retrieve
print(NSUserDefaults.standardUserDefaults().valueForKey("ApplicationIdentifier")!)
if (UIDevice.current.identifierForVendor?.uuidString) != nil
{
self.lblDeviceIdValue.text = UIDevice.current.identifierForVendor?.uuidString
}
From iOS 11 onwards you can use Apple's api: DeviceCheck
class func uuid(completionHandler: #escaping (String) -> ()) {
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
completionHandler(uuid)
}
else {
// If the value is nil, wait and get the value again later. This happens, for example, after the device has been restarted but before the user has unlocked the device.
// https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor?language=objc
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
uuid(completionHandler: completionHandler)
}
}
}
I've tried with
let UUID = UIDevice.currentDevice().identifierForVendor?.UUIDString
instead
let UUID = NSUUID().UUIDString
and it works.

iOS App Update and User Defaults

I've developed a game and I released it to the App Store about 2 months ago (Supernatural Slayer). The game uses user defaults to store player data such as level, xp, gold, etc.
I want to update the game to include review requests which I programmed based on help from hacking with swift. (I also had to switch ad networks since chart boost suspended my account indefinitely for no reason...)
My question is that I seem to remember during development about 4-6 months ago that every time I added another variable to save and load from user defaults it would cause an existing game to crash and I would have to start from scratch by deleting and reloading the game onto my phone to erase user defaults. But now that I'm adding the reviewRequest variable it isn't causing the same error, I'm not sure if I'm not testing correctly and if once I update all of my players will lose their progress...
My code for the save and load functions is below, will this cause an error for existing users that update my app if I add the reviewRequest variable and the save and load lines for it? I've updated both my Mac OS and Xcode since it used to crash for me, so maybe this is why it is not an issue anymore?
let defaults = UserDefaults.standard
var reviewRequest = 0
func save() {
defaults.set(reviewRequest, forKey: "Review Request")
defaults.set(player.name, forKey: "PlayerName")
}
func load() {
player.name = defaults.object(forKey: "PlayerName") as! String
reviewRequest = defaults.integer(forKey: "Review Request")
}
You should try adding a nil check to avoid trying to retrieve an empty key.
func load() {
if defaults.object(forKey: "PlayerName") != nil {
player.name = defaults.object(forKey: "PlayerName") as! String
} else {
//Default name or something similar
}
if defaults.object(forKey: "ReviewRequest") != nil {
reviewRequest = defaults.integer(forKey: "Review Request")
} else {
reviewRequest = 0
}
}

Why doesn't NSUserDefaults work between my app & share extension?

I have an iOS app with a share extension. I am trying to share data between them using NSUserDefaults and App Groups but, while I can write into the NSUD object, read it, and synchronize() without error, reading in the extension always results in nil.
I have an app group, the literal string "group.net.foo.bar" for which both the app & extension have configured under Capabilities -> App Groups. This string is in a constants struct in my app:
struct Forum {
static let APP_GROUP = "group.net.foo.bar"
static let AUTH_KEY = "AUTH_KEY"
}
In the main app I create a UserDefaults object and write to it:
fileprivate lazy var userDefaults: UserDefaults = {
let defaults = UserDefaults()
defaults.addSuite(named: Forum.APP_GROUP)
return defaults
}()
// later
userDefaults.set(apiKey, forKey: Forum.AUTH_KEY)
userDefaults.synchronize()
Creating a new NSUD object after that synchronize() and retrieving the AUTH_KEY works. In the extension, I create an NSUD and try to retrieve the value, to no avail:
private lazy var userDefaults: UserDefaults = {
let defaults = UserDefaults()
defaults.addSuite(named: Forum.APP_GROUP)
return defaults
}()
// later
private func getApiKey() -> String? {
return userDefaults.string(forKey: Forum.AUTH_KEY)
}
// returns nil
In all of my reading of the Apple docs and depressingly-similar questions here on Stack Overflow I can't divine what I've done incorrectly.
Xcode Version 8.0 (8A218a), also tested with Xcode 8.1 Beta 2. Same behavior on simulator andy my iPhone 6s running iOS 10.
Not sure if defaults.addSuite(named: ...) does the same as UserDefaults(suiteName: ...). In my app I use appGroups this way and it works as expected:
// write
if let userDefaults = UserDefaults(suiteName: appGroupName) {
userDefaults.set("---" as AnyObject, forKey: "distance")
userDefaults.set("---" as AnyObject, forKey: "altitude")
...
userDefaults.synchronize()
}
// read
if let userDefaults = UserDefaults(suiteName: appGroupName) {
self.distanceLabel.text = userDefaults.string(forKey: "distance")
self.altitudeLabel.text = userDefaults.string(forKey: "altitude")
}
if you suffer this problem when you try to save data to extension APP by using userDefault,maybe you had written this code : [[NSUserDefaults standardUserDefaults] initWithSuiteName:#"group.xxx.com"];,this code reset default userDefault. Actually,the correct code is : [[NSUserDefaults alloc] initWithSuiteName:#"group.xxx.com"]; enter link description here

How to detect First launch of app on every new version upgrade?

I have a requirement of detecting the first launch of app after the user upgrades the app to a newer version. I need to perform certain task only on first launch of app after the user upgrades the app to a new version. Many links available online but none answer clearly to my query. How to achieve this in Swift 2 , iOS 9.
Most of the answers available says to maintain a key in NSUserDefaults and set its value to false and after first launch make it true. But the problem is after I upgrade my app the variable still will be true and thus my scenario fails on app upgrade. Any help would be much appreciated. Thanks!
Try this:
let existingVersion = NSUserDefaults.standardUserDefaults().objectForKey("CurrentVersionNumber") as? String
let appVersionNumber = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String
if existingVersion != appVersionNumber {
NSUserDefaults.standardUserDefaults().setObject(appVersionNumber, forKey: "CurrentVersionNumber")
NSUserDefaults.standardUserDefaults().synchronize()
//You can handle your code here
}
updating Yogesh's perfect, yet simple solution to swift 4
let existingVersion = UserDefaults.standard.object(forKey: "CurrentVersionNumber") as? String
let appVersionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
if existingVersion != appVersionNumber {
print("existingVersion = \(String(describing: existingVersion))")
UserDefaults.standard.set(appVersionNumber, forKey: "CurrentVersionNumber")
// run code here.
}

How to get a unique device ID in Swift?

How can I get a device's unique ID in Swift?
I need an ID to use in the database and as the API-key for my web service in my social app. Something to keep track of this devices daily use and limit its queries to the database.
You can use this (Swift 3):
UIDevice.current.identifierForVendor!.uuidString
For older versions:
UIDevice.currentDevice().identifierForVendor
or if you want a string:
UIDevice.currentDevice().identifierForVendor!.UUIDString
There is no longer a way to uniquely identify a device after the user uninstalled the app(s). The documentation says:
The value in this property remains the same while the app (or another app from the same vendor) is installed on the iOS device. The value changes when the user deletes all of that vendor’s apps from the device and subsequently reinstalls one or more of them.
You may also want to read this article by Mattt Thompson for more details:
http://nshipster.com/uuid-udid-unique-identifier/
Update for Swift 4.1, you will need to use:
UIDevice.current.identifierForVendor?.uuidString
You can use devicecheck (in Swift 4)
Apple documentation
func sendEphemeralToken() {
//check if DCDevice is available (iOS 11)
//get the **ephemeral** token
DCDevice.current.generateToken {
(data, error) in
guard let data = data else {
return
}
//send **ephemeral** token to server to
let token = data.base64EncodedString()
//Alamofire.request("https://myServer/deviceToken" ...
}
}
Typical usage:
Typically, you use the DeviceCheck APIs to ensure that a new user has not already redeemed an offer under a different user name on the same device.
Server action needs:
See WWDC 2017 — Session 702 (24:06)
more from Santosh Botre article - Unique Identifier for the iOS Devices
Your associated server combines this token with an authentication key that you receive from Apple and uses the result to request access to the per-device bits.
For Swift 3.X Latest Working Code, Easily usage;
let deviceID = UIDevice.current.identifierForVendor!.uuidString
print(deviceID)
You can use identifierForVendor public property present in UIDevice class
let UUIDValue = UIDevice.currentDevice().identifierForVendor!.UUIDString
print("UUID: \(UUIDValue)")
EDIT
Swift 3:
UIDevice.current.identifierForVendor!.uuidString
END EDIT
In swift 4,5
You can get UUID using below code.
print(UIDevice.current.identifierForVendor!.uuidString)
Output
Apart from that you can get multiple properties from connected Device.
UIDevice.current.name // e.g. "My iPhone"
UIDevice.current.model // e.g. #"iPhone", #"iPod touch"
UIDevice.current.localizedModel // localized version of model
UIDevice.current.systemName // e.g. #"iOS"
UIDevice.current.systemVersion // e.g. #"15.5"
Swift 2.2
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.objectForKey("ApplicationIdentifier") == nil {
let UUID = NSUUID().UUIDString
userDefaults.setObject(UUID, forKey: "ApplicationIdentifier")
userDefaults.synchronize()
}
return true
}
//Retrieve
print(NSUserDefaults.standardUserDefaults().valueForKey("ApplicationIdentifier")!)
if (UIDevice.current.identifierForVendor?.uuidString) != nil
{
self.lblDeviceIdValue.text = UIDevice.current.identifierForVendor?.uuidString
}
From iOS 11 onwards you can use Apple's api: DeviceCheck
class func uuid(completionHandler: #escaping (String) -> ()) {
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
completionHandler(uuid)
}
else {
// If the value is nil, wait and get the value again later. This happens, for example, after the device has been restarted but before the user has unlocked the device.
// https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor?language=objc
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
uuid(completionHandler: completionHandler)
}
}
}
I've tried with
let UUID = UIDevice.currentDevice().identifierForVendor?.UUIDString
instead
let UUID = NSUUID().UUIDString
and it works.

Resources