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.
Related
An object that helps a user share data from one place to another within your app, and from your app to other apps.
This is the statement written at the very beginning of UIPasteboard docs. But when I try to use it in two different apps accessing data set by other app I am getting nil everytime
DispatchQueue.global(qos: .background).async {
var i = 1
while(i > 0) {
let v = UIPasteboard.general.string
sleep(1)
print("Task : \(i)")
print("Value: \(v)")
i = i + 1
}
}
I am fetching data in above code and setting data as in below code.
UIPasteboard.general.string = "Hello"
NB: I have tested locally in this app it is setting data
Are you running iOS >=10? There was a privacy change regarding passing value between apps. Try reading the UIPasteBoard api doc : (https://developer.apple.com/documentation/uikit/uipasteboard).
Tl:dr You need to have both apps to be in the same app group (Communicating and persisting data between apps with App Groups)
To note: iOS apps are sandboxed. So the change in iOS 10 just enforces that feature.
EDITED: Since you can't use App Groups (different developer and/or products), you have to send data via a different channel. Try searching urlSchemes or store/fetch through a common server(tedious tho)
Is sharing UserDefaults between iOS and tvOS still possible?
In my Xcode project I use UserDefaults to share data between my iOS target and my tvOS target. I always receive nil values from my UserDefaults when I try to get data back from my tvOS app.
These are the steps I took to share data:
1: Add App Groups for both targets. Both targets use the same App Group ID:
group.nl.mycompany.myappname.
I use the .nl domain but this should be fine since this also worked for my other projects.
2: Confirm both targets have the same deployment target. I tried using 10.0 and 11.0.
3: Validate the myproject.entitlements that everything is set OK.
4: Validate that on developer.apple.com the App Group is enabled for my bundle identifier.
5: Both targets have the same bundle ID. I also tried using 2 different bundle identifiers.
6: The way I write to UserDefaults from my iOS app:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return }
defaults.set("Apple", forKey: "username")
defaults.synchronize()
I confirm this works in my iOS app by getting the value like so:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return nil }
defaults.synchronize()
let name = defaults.string(forKey: "username")
This indeed returns "Apple".
7: Opening my tvOS app and calling this code returns nil:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return nil }
defaults.synchronize()
let name = defaults.string(forKey: "username")
Is it possible that UserDefaults sharing has been removed? Something similar happened to sharing UserDefaults between your phone and watch link here. I also read that the maximum size of UserDefaults is 500kb for the AppleTV but saving this simple string should be fine.
Apple clearly states in the UserDefaults documentation that
With the exception of managed devices in educational institutions, a
user’s defaults are stored locally on a single device, and persisted
for backup and restore. To synchronize preferences and other data
across a user’s connected devices, use NSUbiquitousKeyValueStore
instead.
As it says, you should use iCloud-based NSUbiquitousKeyValueStore for synchronized data storage.
As for its (NSUbiquitousKeyValueStore) limits, the documentation says
The total amount of space available in your app’s key-value store, for a given user, is 1 MB. There is a per-key value size limit of 1 MB, and a maximum of 1024 keys.
Did anyone face an issue with Shared User Defaults via App Groups on iOS 11? I am saving a value in one of the extensions but I am not able to fetch same value via another extension.
In the first extension :
let defaults = UserDefaults.init(suiteName: Constants.commonSuite)
defaults.set("Sample", forKey: "SampleKey")
defaults.synchronize()
In the second extension :
let defaults = UserDefaults.init(suiteName: Constants.commonSuite)
let sampleString = defaults.object(forKey: "SampleKey")
print(sampleString)
Interestingly this thing works fine on iOS 10. Breaks on iOS 11
Had the same issue.
Suitename needs to be the same value as your app group name, not some arbitrary value.
Hope that helps!
I have an iPhone 7 , and I also use it for development. I noticed that in my app , I was storing a session token for auto login purposes into the NSUserDefaults. But sometimes , without removing it the session token was nil and the auto login is not working. This also happens to other apps , where some times it asks me to login again , although I already have. I am guessing that somehow to NSUserDefaults that most of the apps use are being erased somehow. Anyone having a similar issue ?
class func setSessionToken(_ token: String){
let defaults = UserDefaults.standard
defaults.setValue(token, forKey: sessionTokenKey)
defaults.synchronize()
}
class func getSessionToken()->String?{
let defaults = UserDefaults.standard
return defaults.string(forKey: sessionTokenKey)
}
This issue normally occurs when Running an iOS8 or 9 simulator followed by an iOS10 simulator and NSUserDefaults stop working in the simulator.
But, in your case if it is not working in iPhone also . You can try it with latest version of Xcode8 .
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! :)