Swift 3 Migration Cleared NSUserDefault Data - ios

I'm not sure how to use the new UserDefaults class with the new Swift3 changes.
I had this code prior to the migration to swift3 which successfully retrieved the data stored in the userDefaults:
if NSUserDefaults.standardUserDefaults().objectForKey("profileArray") != nil {
profileArray = NSUserDefaults.standardUserDefaults().objectForKey("profileArray") as! [String]
}
With the migration to swift3 the code is now:
if UserDefaults.standard.object(forKey: "profileArray") != nil {
profileArray = UserDefaults.standard.object(forKey: "profileArray")! as! [NSString]
}
The new syntax makes sense but when I run the project the data that was previously stored in the user default seems to be gone.The userdefault.standard... is now returning empty/nil.
How do I retrieve the data that was stored prior to the swift3 migration?
Appreciate the help with moving to swift3!

I don't know if that solves your problem, but your syntax is not very efficient – you retrieve the object from user defaults unnecessarily twice.
The recommended way is optional binding and the dedicated method to get an array:
if let array = UserDefaults.standard.array(forKey: "profileArray") as? [String] {
profileArray = array
}
Im Swift use always String rather than NSString unless you have no choice.

I have finally figured this out. The userDefaults weren't "cleared". but the userDefaults are specific to a device. so with the migration to xcode8 the iOS simulators were also upgraded....and these are technically new devices where userDefaults had never been captured.
I proved this theory by adding a simulator (you can go back and re-install iOS 9.x simulators) and this worked.
I also tested on a real device using iOS 9.x & the new swift 3 code and the defaults persisted on that device.
So problem solved! :)

Related

Swift - UserDefaults setting not getting saved inside framework

I have a framework that is generating a device UUID once and saving it using UserDefaults. The app has access to the UserDefaults and everything works as expected. However, the framework is not accessing UserDefaults in some cases.
I sorted this out on an iPhone 8 using the synchronize() method:
func getDeviceID() -> String {
if let device = UserDefaults.standard.object(forKey: "DeviceID") as? String {
return device
} else {
let device = UUID().uuidString
UserDefaults.standard.set(device, forKey: "DeviceID")
UserDefaults.standard.synchronize() // this line helped with an iPhone 8
return device
}
}
However, on an older iPhone SE 1st generation the issue comes back.
First, why is this happening at all, and why is the synchronize() method seemingly helping in a newer device? (Both phones are running iOS 13)
Are there any known limitations when accessing UserDefaults from within a framework?
If it is failing when you're reading data right after writing the deviceId to UserDefaults
Then it could be related to how UserDefaults actually stores the data to disk.
The actual write to disk is asynchronous and batched automatically by NSUserDefaults.
Check this
So there's a chance that it is slower for older devices running new iOS versions.

How To Use Shared Container/App Groups To Access Files From Other Targets in Swift

I am new to Swift.
I am trying to make a two-app project, where one app contains some data files and the other accesses those files.
The solution, I think, has been to use the App Groups entitlements to allow for this, and access the files through those means. I have been able to follow along with the example here: Communicating and persisting data between apps with App Groups. In particular, the 2nd answer, which is Swift-ish (maybe an older version I guess?). It does seem to work with the right entitlements. So, now the question is how can I access the file from one app, with it being apart of the another? I'm not familiar with the API's and correct functions that I can use (new to Swift, as I said).
The apps are basic. Setup as single view applications, with everything defaulted except the ViewController's, entitlements, and one has the test data. The Xcode project structure is:
testingData/
testingData/
testingData.entitlements
TestData/
testdata.txt
AppDelegate.swift
ViewController.swift
Main.storyboard
Assets.xcassets
LaunchScreen.storyboard
Info.plist
sharedContainerTest/
sharedContainerTest.entitlements
AppDelegate.swift
ViewController.swift
Main.storyboard
Assets.xcassets
LaunchScreen.storyboard
Info.plist
Products/
testingData.app
sharedContainerTest.app
The entitlements are both the same. They each have App Groups enabled, with the same string: group.com.example.name. On the testingData target, the ViewController has the following chunk in the viewDidLoad function from that example (modified for Swift 4.x):
var userDefaults = UserDefaults(suiteName: "group.com.example.name")!
userDefaults.set("user12345", forKey: "userId")
userDefaults.synchronize()
On the sharedContainerTest target, its ViewContoller has
var userDefaults = UserDefaults(suiteName: "group.com.example.name")
if let testUserId = userDefaults?.object(forKey: "userId") as? String {
print("User Id: \(testUserId)")
}
in its viewDidLoad function. As I said, this seems to work, but now what do I need to add to this to access the testdata.txt file from the testingData app? Does it need to be stored as a variable, perhaps? Or is there a Bundle object that can do this?
If this has been answered elsewhere, please kindly point me to it and I'll take this down.
After some trial and error, the answer is as follows:
Instead of passing in the string "user12345", you need to pass in the URL to the file you want to read for the userDefaults.set method as follows:
var userDefaults = UserDefaults(suiteName: "group.com.example.name")!
userDefaults.set(Bundle.main.url(forResource: "testdata", withExtension: ".txt"), forKey: "userId")
userDefaults.synchronize()
Then in the receiver app, you call that object and set the URL:
let userDefaults = UserDefaults(suiteName: "group.com.example.name")
let test = userDefaults?.object(forKey: "userId") as? String
let testURL = URL(fileURLWithPath: test!)
From here you can read in the contents as normal.

Firebase's snapshot.value cast to Bool suddenly returning nil

So my code was working perfectly fine but today it is returning nil when I try to cast Firebase's snapshot.value to a Bool. I haven't made any change to code at all. But it works fine if I cast it to a String (as? String returns "true").
Same issue at another place where I was casting to [String: Bool].
One thing that is different from earlier, I installed Xcode 10 beta 3 today (Xcode 9.4 is installed too). Both Xcode versions are returning nil. Here's the code:
if let currentUserId = Auth.auth().currentUser?.uid{
Database.database().reference().child("users/\(currentUserId)/share").observe(.childAdded) { (snapshot) in
let data = snapshot.value as? Bool
if data == true{
self.showNotificationsBadge()
}
}
}
Edit: Snapshot is not nil. You can see that in the console pane. I have printed both snapshot.key and .value.
Edit 2: I'm sorry if my question caused any confusions. The main purpose of this question is to know the problem, is it Firebase SDK or Xcode. Because it was working perfectly till now. I know how I can tackle this and how can I convert a String to a Bool.
Edit 3: Thank you everyone for your answers. I'm sorry I was unable to deliver my thoughts correctly. I just want to know about this unpredicted behavior and why is it happening? Who is the culprit here? Xcode, Firebase SDK? This code worked PERFECTLY FINE for about a month. I'm not asking for a solution, I'm just curious about this behavior. It would be great if someone from team Firebase can explain this issue.
Edit 4: Removed image and added code. Also figured out the issue thanks to #Jay's comment.
You need check if the snapshot is nil before checking the value.
if let data = snapshot.value as? Bool {
....
}
This happened to me just recently. I discovered that I had quotes around the child value, eg. "true". Remove the quotes and the solution from #Maximo should work as expected.

Array data values load correctly in simulator but returns Nil values on phone

Hello folks and thanks for your help,
I am currently running an Xcode 8 project using swift 3 and Firebase Database. I am pulling over data from a CLLocationSearch using Core Location Data from a previous table Search UI. The data comes over and populates the "StoreUI" where I have this function addBtn that kicks off the code below to send the data to Firebase Database.
My issue is as follows: When I run the app with xCode's simulator, everything works fine. The data pulls together, fills the array properly, and when I break the code after setting up the array, I see all the proper values before it eventually posts to firebase. Again, using the simulator everything works 100%.
However, when I run the app from the phone, nothing happens in the array. All the values come back as nil for each segment resulting as 8 nil values in my array. Because the array has nil values it will crash the app during the post command.
What am I doing wrong, or what do I need to do, to get this running correctly both in the simulator and the phone testing?
So far I tired moving the variable setup outside the function, but I get the same results. I was thinking of moving the "let post" code into it's own function, but I am not sure that will help.
Thanks again for your help!
#IBAction func addBtn(_ sender: AnyObject) {
// Save Store Info.
let uniqueRef = storeRef.childByAutoId()
geoFire = GeoFire(firebaseRef: geoRef)
let lat = storeData?.placemark.coordinate.latitude
let long = storeData?.placemark.coordinate.longitude
let post : [String: String] = ["StoreName" : (storeData?.name)!,
"subThouroughfare" : (storeData?.placemark.subThoroughfare)!,
"Thouroughfare" : (storeData?.placemark.thoroughfare)!,
//"subLocality" : (storeData?.placemark.subLocality)!,
"locality" : (storeData?.placemark.locality)!,
"subAdministrativeArea" : (storeData?.placemark.subAdministrativeArea)!,
"administrativeArea" : (storeData?.placemark.administrativeArea)!,
"postalCode" : (storeData?.placemark.postalCode)!,
"phoneNumber" : (storeData?.phoneNumber)!
]
let key: String = uniqueRef.key
geoFire!.setLocation(CLLocation(latitude: lat!, longitude: long!), forKey: key)
uniqueRef.setValue(post)
}
This was an issue with coding my 3rd party developer did, having issues with the MKMapItem. He has fixed my issue and this case is closed.

Unable to Write Swift Dictionary to NSUserDefaults in Beta 5

I have been working on an app in which I user NSUserDefaults to persist a Dictionary for use elsewhere in the program. This been working perfectly since Beta 1. Now with the latest update (beta 5) this no longer works. It appears they have removed the ability to persist a swift Dictionary in this manner. However, I can persist an NSDictionary. Here is a code snippet that worked 2 days ago and now is broken.
var userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(allPlayersDict, forKey: "playerDict")
userDefaults.synchronize()
This code does not write anything to the NSUserDefaults, not even the key, when checked using:
var testDict = NSUserDefaults.standardUserDefaults().dictionaryRepresentation()
println(testDict)
Has anyone else experienced this or have a solution?

Resources