Is sharing UserDefaults between iOS and tvOS still possible? - ios

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.

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.

Shared User Defaults via App Groups in iOS 11 not working

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!

iPhone 7 NSUserDefaults turning into nil

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 .

XCTest : Not able to delete user default data of framework target

Target is iOS8 dynamic framework(UserDataFramework) and I saved a data on UserData class. Now removing the saved data for checking as App-Delete-Scenario. But not able to delete data from testTarget (UserDataFrameworkTest) to framework data (UserDataFramework).
import XCTest
#testable import UserDataFramework
func testAppDeleteFunctionality() {
UserData.saveData()
let kCheckFirstRun = "com.key.checkFirstRun"
// When app deleted, cleared the user default
let userDefault = NSUserDefaults.standardUserDefaults()
userDefault.removeObjectForKey(kCheckFirstRun)
userDefault.synchronize()
let checkData = userDefault.valueForKey(kCheckFirstRun)
XCTAssertNil(checkData)
}
This problem due to switching simulators from iOS 9 to iOS 10. I found this bug from Apple side. Just restart the system and userdefault can delete and store value as well.

Resources