Watchkit Extension Cannot Read from iCloud - swift

I am looking for the watchkit extension to read a file from iCloud document storage. I wrote the file from the iOS app which is also able to read it. I am leveraging a shared class so the code is the same. The problem is that on the watch, the URL for the cloud container returns nil.
static func readFromFile(fileName:String) -> String?
{
let fileManager = FileManager.default
var cloudURL = fileManager.url(forUbiquityContainerIdentifier: nil)
cloudURL = cloudURL?.appendingPathComponent("Documents/\(fileName)")
do
{
return try String(contentsOf: cloudURL!)
} catch {
print (error)
return nil
}
In the case above, cloudURL is nil on the watch but not the phone. For the forUbiquityContainerIdentifier I checked that the same identifier is used for both watch and phone. I have also tried to directly enter the name rather than using nil and letting it grab it from the entitlements. The format of the container:
iCloud.com.company.app
It's my understanding that Watch OS 3 is supposed to be able to use iCloud.
Update:
I printed out let token = fileManager.ubiquityIdentityToken and I get the following:
WatchKit Extension[390:687462] [default] [ERROR] error while getting
ubiquityIdentityToken: Error Domain=NSCocoaErrorDomain Code=4099 "The
connection to service named com.apple.bird.token was invalidated."
UserInfo={NSDebugDescription=The connection to service named
com.apple.bird.token was invalidated.}

I have officially heard back from Apple that this is not a supported feature of Watch OS. This runs contrary to their own documentation which I let them know about. Hopefully others see this response and it can save a ton of time that I wound up wasting.

It was 2021. Apple documentation still says that iCloud Drive methods can be used by watchOS 2.0+, but url forUbiquityContainerIdentifier returns nil :(

Related

Error code -1009 and "Operation not supported by device" when using URL(...) on watchOS

Let's say I have the following code:
guard let URLData = URL(string: "https://www.bing.com") else {
print("Unable to access \(URLString)")
return
}
When I run this on my Apple Watch Series 7 (Connected to stable WiFi), it throws a bunch of stuff in the console, such as Error getting network data status Error Domain=NSPOSIXErrorDomain Code=19 "Operation not supported by device" and NSURLConnection finished with error - code -1009.
I know error code -1009 means there was a failure in connection... Why is this happening? My Apple Watch is definitely connected to the internet. Same with my iPhone. Also, I'm now having concerns about URL access on watchOS, due to their "Operation not supported by device" thing and the fact that stuff like WebKit is already locked from development on watches...
IDK! Help!

How to fix loadPersistentStores from crashing on load

I'm trying to develop an app on Xcode that uses core data, however, when I submit the app for test review it crashes on their simulators, but not mine. What's going on here?
For starters, I've recently finished making an app that took a while to develop and went to submit it to iTunes connect. My App was rejected because "we were unable to review your app as it has crashed on launch". I received and symbolicated the line where the crash happens.
https://imgur.com/a/3iX6pqc
The crash happens on line 81 in my App Delegate exactly where I go to get my persistent container and load it in.
https://imgur.com/EzQvnSQ
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "AlarmSavedData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Here!")
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
I expected that the local database would load in just fine for them, but for some reason, I'm the only one that the database loads in for. I'm able to use it but when the Apple Review team tries to run it, it crashes. Any ideas on what's happening and to do to fix it? The goal is for every user to have their own local database using core data to store their created data.
Crash Log : https://imgur.com/a/rx9doSR
Try resetting the simulator. That fixed it for me:
First of all, ensure that you are testing your app with a simulator of iOS 12.1.4 as indicated in the crash report. The crash report indicates the Hardware to be 1xxx which is apparently some kind of placeholder, not helpful.
I can't explain the crash yet, but here are some clues that might help you find it.
In Apple's TN2151, regarding the EXC_BREAKPOINT type of crash which is what you have, it is stated:
Swift code will terminate with this exception type if an unexpected condition is encountered at runtime such as:
a non-optional type with a nil value
a failed forced type conversion
Putting this together with the fact that the crash occurs in loadPersistentStores() implies that maybe it is not crashing for you because you already have a persistent store on the simulator's simulated disk – that is, you are not a first-time user. App Review is, of course, a first-time user. So you should remove your app's data, in particular the persistent store file(s), from the simulator and test again. Now your test will be more like App Review's.
You should also probably read this answer which discusses a somewhat similar situation.

iOS 11 MDM: managed app configuration doesn't work

let serverConfig = UserDefaults.standard.dictionary(forKey: "com.apple.configuration.managed") print("serverConfig count:
\(String(describing: serverConfig?.count))")
Above code always returning nil for iOS 11.
I want transfer few data to managed application using MDM portal via managed configuration as per the apple developer document they didn't update anything in to it.
I don't know what I'm doing wrong.Please help me out with this.

iCloud syncing of Core Data with Ensembles

My plan is to purchase Ensembles 2 (to take advantage of speed/efficiencies etc) but am trying to make sure that I will be able to get it to work (in a test Swift project) first. To do this I am experimenting with v1.
Using the Simple Sync with Swift as a guide I have incorporated ensembles into my xcode project.
The data from the app does appear to be getting stored in iCloud as when I delete the app and then re-add it, leeching and then syncing does restore the correct data from iCloud. My trouble is that testing with a second device (signed in to the same apple/iCloud account) does the same thing with its own data. The data from the 2 devices is never merged. However the data created on each device is restored to its own device after reloading the app.
Does anyone know how this could be?
Am wondering if the problem might be the store url that I am generating. A lot of the Core Data Stack set up is now done automatically in Swift 3+ (NSPersistentContainer) and so these things do not need to be generated by the user. Here is how i am generating the variables for store url and model url to use when setting up my ensemble:
var storeDirectoryURL: URL {
return try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
var storeURL: URL {
return storeDirectoryURL.appendingPathComponent("VsSyncTest.sqlite")
}
// Setup Ensemble
let modelURL = Bundle.main.url(forResource: "VsSyncTest", withExtension: "momd")
The only way I can imagine that would happen is if either you are using different iCloud accounts, or you have a different ensemble identifier for each device.
Note also that the iCloud document sharing can be stubborn. Just because the files get added to the container locally does not mean they will immediately transfer to the other device. (CloudKit backend is much better in that respect, but only in E2.)
If you are testing with the simulator, using Debug > iCloud > Trigger Sync in Simulator may help.

Problems with Watchkit Extension NSURLSession in Swift

I'm trying to call my local API to turn off my house lighting from my watch, but I'm having trouble with calling a url from within the watchkit extension.
In my ViewController (iPhone app) I've got exactly the same code (which works), but somehow it doesn't work when I call it in the InterfaceController from the Watchkit Extension.
#IBAction func confirmTapped() {
let url = NSURL(string: "http://homeserver.local/api/lights/4/0")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
print(NSString(data: data!, encoding: NSUTF8StringEncoding))
}
task!.resume()
}
I've also attached a gist: https://gist.github.com/f72125cd4678069af7af.git
Am I missing something obvious?
While Frederick's advice was solid advice for WatchKit 1, openParentApplication:reply is no longer available in WatchKit 2. With WatchKit 2, you would instead definitely open the NSURLSession in the WatchKit App Extension itself, because using a WCSession means that the URL would only be opened if and when the iPhone application itself is running, either in the foreground or if it happens to still be running in the background because the system has not closed it.
It is possible that the problems you were experiencing were because you needed to add the 'Allows Arbitrary Loads' property on the WatchKit App Extension. See discussion in this possibly related thread: NSURLSession returns data as Null on Watch OS2 using Objective-C
I don't know if it's a good thing to start a NSURLSession from within the Watch extension. If the request takes to long, it will be cancelled.
Maybe you could ping the watch via openParentApplication:reply and handle the NSURLSession on your iPhone app.