iOS App Update and User Defaults - ios

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
}
}

Related

Data in my iOS app vanishes after a couple of days

I have an iPhone app that stores data locally in an array. I was testing the app on my actual iPhone and it works fine except that the data vanishes after a couple of days. I am curious to see if this has happened to anyone else and if a solution was found. Here is a sample of the code that stores the data:
var doctorsArray = [Doctors]()
func storeDoctorsArray() {
doctorsArray.sort(by: {(a: Doctors, b: Doctors) -> Bool in
return a.firstName < b.firstName
})
let defaults = UserDefaults.standard
let data = NSKeyedArchiver.archivedData(withRootObject: doctorsArray)
defaults.set(data,forKey: "stored_doctors_data")
defaults.synchronize()
}
And to get data:
func loadDoctorsArray() {
if let storedArray = UserDefaults.standard.object(forKey: "stored_doctors_data") as? Data {
doctorsArray = NSKeyedUnarchiver.unarchiveObject(with: storedArray) as! [Doctors]
Again as mentioned, the app works fine and the data stores and displays as expected but vanishes after a couple of days.
If you delete the app it clears the contents of UserDefaults. Other than that, or resetting the simulator, UserDefaults should persist between runs.
How/when are you calling your loadDoctorsArray() function

Transfer data from project to widget in swift

At my project i need to send user id's to widget in iOS. But for do that, my user needs to open application once. Without opening, information stays only 1 day, after that it vanishes and widget stops showing information and await for opening application.
For do that i used appGroup.
What is the correct way to use transfer data from my project to widget?
Swift 5
Follow these steps to pass data from the host app to extensions.
Select project target > Capabilities > add new app group (if you have enabled permissions for your developer account otherwise enable that first)
Select the extension target and repeat the same.
if let userDefaults = UserDefaults(suiteName: "group.com.yourAppgroup") {
createEventDic.removeAll()
let eventDic = NSMutableDictionary()
eventDic.setValue("YourString", forKey: "timeFontName")
createEventDic.append(eventDic)
let resultDic = try? NSKeyedArchiver.archivedData(withRootObject: createEventDic, requiringSecureCoding: false)
userDefaults.set(resultDic, forKey: "setWidget")
userDefaults.synchronize()
} else {
}
Now go to your app extension and do these steps to get the passed data.
if let userDefaults = UserDefaults(suiteName: "group.com.yourAppGroup") {
guard let testcreateEvent = userDefaults.object(forKey: "testcreateEvent") as? NSData else {
print("Data not found in UserDefaults")
return
}
do {
guard let eventsDicArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(testcreateEvent as Data) as? [NSMutableDictionary] else {
fatalError("loadWidgetDataArray - Can't get Array")
}
for eventDic in eventsDicArray {
let timeFontName = eventDic.object(forKey: "timeFontName") as? String ?? ""
}
} catch {
fatalError("loadWidgetDataArray - Can't encode data: \(error)")
}
}
Hopefully, it will help. Cheers!
For do that i used appGroup.
What is the correct way to use transfer data from my project to
widget?
What you did so far (App Grouping) is one of the steps that you should follow. Next, as mentioned in App Extension Programming Guide - Sharing Data with Your Containing App:
After you enable app groups, an app extension and its containing app
can both use the NSUserDefaults API to share access to user
preferences. To enable this sharing, use the initWithSuiteName: method
to instantiate a new NSUserDefaults object, passing in the identifier
of the shared group.
So, what you have to do so far is to let the data to be transferred by the UserDefautls. For instance:
if let userDefaults = UserDefaults(suiteName: "group.com.example.myapp") {
userDefaults.set(true, forKey: "myFlag")
}
thus you could pass it to the widget:
if let userDefaults = UserDefaults(suiteName: "group.com.example.myapp") {
let myFlag = userDefaults.bool(forKey: "myFlag")
}
And you follow the same approach for passing the data vise-versa (from the widget to the project).
In Xamarin Forms, we need to use DI to pass the data to the ios project then we can put it into NSUserDefaults
Info: Grouping application is mandatory
Xamarin iOS project - Putting Data into NSUserDefaults
var plist = new NSUserDefaults("group.com.test.poc", NSUserDefaultsType.SuiteName);
plist.SetBool(true, "isEnabled");
plist.Synchronize();
Today Extension - Getting data from NSUserDefaults
var plist = new NSUserDefaults("group.com.test.poc", NSUserDefaultsType.SuiteName);
var result = plist.BoolForKey("isEnabled");
Console.WriteLine($"The result of NSUserdefaults: logesh {result}");

Saving data (numbers) in an iOS app?

I'm learning application development working on a quiz game. I'd like to add statistics to the game. For example, the average score since the app has been downloaded. How can I store the scores on the device in order to reuse them after the app has been closed?
You should take a look at UserDefault. It's basically a dictionary that persists until the user uninstalls your app. I like to write a wrapper around it to get strong typing and ease of reference:
struct Preferences {
static func registerDefaults() {
UserDefaults.standard.register(defaults: [kAverageScore: 0])
}
// Define your key as a constant so you don't have to repeat a string literal everywhere
private static let kAverageScore = "averageScore"
static var averageScore: Double {
get { return UserDefaults.standard.double(forKey: kAverageScore) }
set { UserDefaults.standard.set(newValue, forKey: kAverageScore) }
}
}
Here's how to use it: before you call it for the first time in your app, you must register the defaults. These are the values that your app ships with. On iOS, it only really matters for the very first time the user launches your app. On OS X, do this every time your app starts because the user can delete the app's preferences from ~/Library/Application Support.
// You usually do this in viewDidLoad
Preferences.registerDefaults()
From then on, getting and setting the property is easy:
let averageScore = Preferences.averageScore
Preferences.averageScore = 5.5
You should take a look at UserDefaults
Example
let defaults = UserDefaults.standard
defaults.set(25, forKey: "Age")
defaults.set(true, forKey: "UseTouchID")
defaults.set(Double.pi, forKey: "Pi")
To read values back
let age = defaults.integer(forKey: "Age")
let useTouchID = defaults.bool(forKey: "UseTouchID")
let pi = defaults.double(forKey: "Pi")
UserDefaults

Accessing and storing viewController variables in appDelegate

Here's my problem: I have several variables in my viewController, for example:
var money = 0.0
I want to store this value on the device as soon as the app is going to terminate. To do this, I need to be able to acces the variable in the appDelegate and use it in 'applicationWillTerminate'. I then want it to be stored on the device and when the app restarts I want to be able to acces the stored value again. How do I do this in (swift) Xcode 8.2?
"applicationWillTerminate" as Apple docs say, is no more called in modern app unless You set a specific flag in plist.
The correct approach is to store values in NSUserDefault:
For swift 3.0:
1) let's create a class to manage money, and saving them:
class MoneyManager: NSObject {
private let MONEY_KEY = "money"
static let sharedInstance = MoneyManager()
var money: Float? {
get {
let returnValue = UserDefaults.standard.object(forKey: MONEY_KEY) as? Float
return returnValue
}
set(newValue){
if newValue == nil {
UserDefaults.standard.removeObject(forKey: MONEY_KEY)
} else {
UserDefaults.standard.set(newValue!, forKey: MONEY_KEY)
}
UserDefaults.standard.synchronize()
}
}
}
2) to use it in every controller you need:
let money = MoneyManager.sharedInstance.money
if money == nil{
MoneyManager.sharedInstance.money = 1000
}
As every time we call UserDefaults.standard.synchronize(), we are safe everything is saved.

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.
}

Resources