I created a WatchKit Application with the default XCode Template.
I added an app group entitlement to the iOS Target, to the Watchkit App Target and to the Watchkit App Extension Target. (this is the app group name: group.com.lombax.fiveminutes)
Then, I tried to access the shared folder URL with both the iOS App and the WatchKit Extension:
Extension:
#implementation ExtensionDelegate
- (void)applicationDidFinishLaunching {
// Perform any final initialization of your application.
NSURL *test = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.lombax.fiveminutes"];
}
iOS App:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSURL *test = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.lombax.fiveminutes"];
// ...
}
However, the test NSURL is different:
On iOS:
file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/38B983DB-342F-4A47-8C26-5D2C92CDB666/data/Containers/Shared/AppGroup/8DEE182E-AFE6-47DD-BA2B-6B0520158A8B/
on Watch:
file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/CECB5EFC-7FBD-4C84-A878-1314CB7CF211/
And for this reason I'm unable to share data between the iOS App and the WatchKit Extension.
I cannon try on a real device since I don't have WatchOS 2.0 on my Apple Watch.
Any advice?
Thanks
UPDATE
I did some other tests:
Installed WatchOS 2, the issue still persists on real devices.
This is the store url for my iPhone:
NSURL
* #"file:///private/var/mobile/Containers/Shared/AppGroup/3D05D159-94D6-409C-9A38-90E0830D0C3F/FiveMinutes.sqlite"
And this is the store url for my Watch:
NSURL
* #"file:///private/var/mobile/Containers/Shared/AppGroup/F1E89377-F456-4FC2-BAAC-3DD705EF381A/FiveMinutes.sqlite"
The two apps reads and write to-from two different .sqlite files.
On simulator, if I hard-code one of the URLs, both iOS simulator and Watch simulator are able to read-write the same .sqlite file and share the content. However, this is not possible on real devices since the Watch extension cannot write to the iOS path:
URL:file:///private/var/mobile/Containers/Shared/AppGroup/3D05D159-94D6-409C-9A38-90E0830D0C3F/FiveMinutes.sqlite options:(null) ... returned error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2} with userInfo dictionary {
reason = "Failed to create file; code = 2";
}
Ok, I think I've found my answer. I remembered that with the transition to Watch OS 2 the extension code is now executed directly on the Apple Watch, and no more on the paired iPhone. So it's seems obvious that the two devices doesn't share the same storage.
The first thing I did was to create a new project, starting from a base iOS Project, and then adding a Watch OS 1 (old version) App Target.
In this case, the directories were identical and they could communicate:
Watch Path: file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/30B39103-CEEB-4C64-9531-FB27DC40180D/
iOS Path file:///Users/Lombardo/Library/Developer/CoreSimulator/Devices/BF52D613-25FF-4092-A5B2-9C3F1B616160/data/Containers/Shared/AppGroup/30B39103-CEEB-4C64-9531-FB27DC40180D/
Then, I did the first thing every programmer should do: read the docs.
In the FIRST PAGE of the WatchOS 2 transition guide there is this sentence:
Your extension now stores files and data on Apple Watch. Any data that is not part of your Watch app or WatchKit extension bundle must be fetched from the network or from the companion iOS app running on the user’s iPhone. You cannot rely on a shared group container to exchange files with your iOS app. Fetching files involves transferring them wirelessly to Apple Watch.
Related
I'm working in a view where I should customize it regarding if there is a Watch Version of the app.
I know that I can use:
[[WCSession defaultSession] isWatchAppInstalled]
(but that is not what I want because the user can uninstall the app in the Apple Watch but the iOS app will still have the Watch Version available to install)
also:
[[WCSession defaultSession] isPaired] is not my case.
Actually isWatchAppInstalled is the correct way to deal with this scenario.
As the documentation states:
The user can choose to install only a subset of available apps on Apple Watch. The value of this property is true when the Watch app associated with the current iOS app is installed on the user’s Apple Watch or false when it is not installed.
https://developer.apple.com/documentation/watchconnectivity/wcsession/1615623-iswatchappinstalled
The property does not look inside the bundle to see if a Watch app is available. It will only return true if the Watch app is installed on the currently paired Apple Watch. If the user deletes the Watch App from the watch it will return false.
After checking the current project that I'm working on and also testing creating two new projects to check the bundles (one project with watch and the another without watch included), I saw the difference between them:
The difference is that the project with the watch included has a Build Phase that has the Watch app embedded in the subdirectory called "Watch".
Also we can see the "Watch" folder when Showing the Package Contents of the build in Finder:
So the condition whenever that iOS app has Watch embedded in code is:
+ (BOOL)isWatchAppEmbedded
{
NSString *watchPath = [NSString stringWithFormat:#"%#/%#", [NSBundle mainBundle].resourcePath, #"Watch"];
BOOL isDirectory = YES;
if ([[NSFileManager defaultManager] fileExistsAtPath:watchPath isDirectory:&isDirectory]) {
return YES;
}
return NO;
}
I have been consistently unable to get the HealthKit requestAuthorization dialog to appear. This has been the case for two different phones with different OS versions and different installation methods (see below). The requestAuthorization code is standard boilerplate code, such as:
HKCharacteristicType *genderType = [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierBiologicalSex];
[self.healthStore requestAuthorizationToShareTypes:nil readTypes:[NSSet setWithObjects: genderType, nil] completion:^(BOOL success, NSError *error) {
NSLog(#"requestAuthorization completion block");
}];
I've tried a fresh app with just that code, tried different parameters for the requestAuthorization method call, and have tried resetting our systems in a number of different ways: app uninstall, resetting settings, erasing phone content and setting with existing partial backup; erasing phone content and settings with a full backup, erasing content and settings and starting phone as a new phone. Have tried multiple different readTypes and shareTypes. Each time requestAuthorization is called on a fresh app install, the following error code appears:
"Error occurred = Error Domain=com.apple.healthkit Code=4 "Missing
com.apple.developer.healthkit entitlement." UserInfo=0x7fa748534b00
{NSLocalizedDescription=Missing com.apple.developer.healthkit
entitlement.}"
However, the HealthKit entitlements are set in both Capabilities on xcode as well as Capabilities in the App ID on Apple Developer: Certificates, Identifiers, and Profiles.
- Questions: How do I fix this issue so the requestAuthorization dialog appears? What is a possible cause of phones getting into a state where they get this error consistently?
- Phones used: iPhone 6, iPhone 6 Plus
- OS Versions used: iOS 9.0 / 9.0.2 / 9.1 / 9.2
- Methods of loading app onto phone: iOS App Store, Watch App App Store, TestFlight, xcode
- Situations where this appears to not be broken: With our users' (non-development) phones installing via the App Store.
A few things could be causing this. First, your provisioning profiles could be out of date with what you are actually seeing on the web portal. Try refreshing these manually from XCode.
Does your app have an app extension or watchOS app that uses the HealthKit framework? Each of extension/watch app will have their own profiles on the web portal, and if the HealthKit framework has been added to any of them without having the entitlement added as well then this could also cause the error.
I tried to implement universal links in my application, but it's not working. I uploaded the apple-app-site-association file to my server, i can access it.
(MIME type: application/json)
Here is the content of the apple-app-site-association file:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "L7LAMSGWAK.com.example.app",
"paths": [
"*"
]
} ]
}
}
I turned on the Associated domains in my application, and i added these domains:
applinks:example.com
applinks:demo.example.com
(i uploaded the apple-app-site-association file to the demo.example.com domain too)
In the AppDelegate.m file i wrote this:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
NSURL* openedUrl = userActivity.webpageURL;
NSString* urlString = [openedUrl.absoluteString lowercaseString];
return YES;
}
And i set a breakpoint into this method to check if its called, but its never called. If i click on for example this link: https://demo.example.com/asd its just open the safari, but i never see the banner to open the link in my application. I really dont know whats wrong.
Try to delete and reinstall the app. iOS processes associated domain details and attempts to read the apple-app-site-association file at app install. It worth trying it with a real device (instead of the Simulator) and check device logs (Xcode > Window > Devices and select your device). If you see anything like ### Rejecting URL 'https://demo.example.com/apple-app-site-association' for auth method 'NSURLAuthenticationMethodServerTrust': -6754/0xFFFFE59E kAuthenticationErr, then probably your site's certificate is not good enough for iOS or there is some other issue with downloading your association file.
Kind of a late answer, but I had the same problem when I was trying to implement universal links. The solution for me was to test using a real device.
The Apple documentation lists that it's possible to test on a simulator, but with my own experience, it hasn't been possible.
You can test universal links in Simulator or on a device. Source - Apple
I also tried using Branch.io and they state that it's not possible to test using a simulator.
You should also verify that you're using a device that's running iOS >= 9.2
My app has the following targets:
iOS App
Today Extension
Apple Watch App
Apple Watch Extension
All of these targets have the "App Group" capability with the correct group selected. In the main iOS app, I synchronize the settings like so:
NSUserDefaults * sharedDefaults = [[NSUserDefaults alloc]initWithSuiteName:#"group.foo.bar"];
if([sharedDefaults respondsToSelector:#selector(setObject:forKey:)]){
// Example Value
[sharedDefaults setObject:#"TestString" forKey:#"TestString"];
[sharedDefaults synchronize];
}
The Today Extension uses the following to get the settings, which works just fine:
NSUserDefaults * defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.foo.bar"];
NSLog(#"String: %#", [defaults stringForKey:#"TestString"]);
// String: TestString
The watch extension uses the same line of code to get the settings as the today extension, however it does not work. In the console I see String: (null). What am I doing incorrectly?
Did you create your watch target as a watchOS WatchKit Application (watchOS 2) or as an iOS Application Extension (watchOS 1)? If targeting for watchOS 2, according to the transition guide: You cannot rely on a shared group container to exchange files with your iOS app. Fetching files involves transferring them wirelessly to Apple Watch.
Please refer to settings bundle not working on watchOS 2 Apparently the docs are not that clear on what can and cannot be done with Preferences, the only way I found was to ensure that I copy the required user defaults over and keep them synced manually (NSUserDefaults not working on Xcode beta with Watch OS2).
I have developed iPhone app with watchOS 2 app, but I met a strange problem.
Everything is ok before I localize InfoPlist.strings of watchOS 2 target (not watchOS 2 extension target).
If I localize InfoPlist.strings, the app will get the error
Error Domain=WCErrorDomain Code=7007 "WatchConnectivity session on
paired device is not reachable."
UserInfo={NSLocalizedDescription=WatchConnectivity session on paired
device is not reachable.}.
I have no idea why. Here is my test project: https://dl.dropboxusercontent.com/u/31258390/TestApp.zip
Thanks!
I just wanted to say that I've had the same issue, and I found a radar (#23096604) detailing a similar problem:
If you have localization on the watch extension (even empty strings files), the only part of WCSession that seems to work is sending data via the applicationContext. The transferUserInfo method just queues up data and it is never sent. The sendMessage function is also unreliable in this case.
Closed as duplicate of 22682390.