WatchKit App as Controller for iOS Device - ios

Is it possible to have a WatchKit app be a controller for it's parent iOS app?
I want to have buttons on the WatchKit app that cause an action on the iPhone app, but I can't find a way to get that going. Tried sending a notification - no joy.
Tried using WKInterfaceController.openParentApplication but that doesn't work either.
Can someone point me in the right direction?
Thanks.
Ken

See example from the blog of #NatashaTheRobot:
http://natashatherobot.com/watchkit-open-ios-app-from-watch/

Here is what my handleWatchKitExtensionRequest looks like. Check the spelling of this method in your code. You didn't spell it right in the comment and that would cause a unrecognized selector crash.
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
NSString* command = [userInfo objectForKey:#"command"];
NSString* uuid = [userInfo objectForKey:#"uuid"];
if (command == nil)
{
reply(nil);
}
else
{
if ([command isEqualToString:#"startTrip"])
{
...
//The uuid of the trip is returned to the watch app
reply(#{#"uuid": uuid});
}
else if ([command isEqualToString:#"stopTrip"])
{
...
reply(nil);
}
else if ([command isEqualToString:#"pauseTrip"])
{
...
reply(nil);
}
else if ([command isEqualToString:#"resumeTrip"])
{
...
reply(nil);
}
}
}

Related

ios Handoff missing NSUserActivity in launchOptions?

I have implemented Handoff in our App and it is working fine for web to app handoff and vice versa, when the app is running in foreground or in the background.
However if the app is not running, then if the user launches the app from a web to app handoff, in the launchOptions dictionary, I get the UIApplicationLaunchOptionsUserActivityDictionaryKey, but the reference to the activity is missing.
See screenshot:
As you can see I'm getting only the ID for the NSUserActivity.
Is this a bug in iOS 9 ?
Is there a way to get a reference to the activity by using the id?
Edit, here is the code, although I don't think this is relevant
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions && [[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
__block NSUserActivity *activity;
NSDictionary *userActivityDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
if (userActivityDictionary) {
[userActivityDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSUserActivity class]]) {
activity = obj;
}
}];
}
//app was started by URL (deep linking), check parameters
if (activity) {
NSURL *url = activity.webpageURL;
//resume from URL
}
}
return YES;
}
Ok,
I've submitted a TSI about this to Apple, and it seems that this is not a bug, but by design.
You can resume your activity in the application:continueUserActivity:restorationHandler delegate, which in my case was not called.
Well, my mistake was that you need to return YES in the application:didFinishLaunchingWithOptions: method, else if you return NO, the application:continueUserActivity:restorationHandler is not called.
We had implemented FB in our app, so we where returning [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions] which would return NO.
I have changed our code in the application:didFinishLaunchingWithOptions: function to this
if (launchOptions && [[launchOptions allKeys] containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
return YES;
}
else {
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
This way the application:continueUserActivity:restorationHandler delegate is being successfully called and the activity can be resumed successfully.

Print NSlog output when app is closed

I am having an iOS application where I am receiving Push Notifications. I have the following code in AppDelegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ( application.applicationState == UIApplicationStateActive )
{
// app was already in the foreground
}
else
{
NSLog(#"Received push notification");
}
}
I am running the app in Xcode with my iPhone. When the app is in background I am able see the NSLog when the notification comes.
When I close the app i.e removing it from opened apps in iOS the debugger session in Xcode stops and I can't see the NSlog after that even after receiving notification on my iPhone for the app when the app is closed (Push Notifications can come to app even if it is closed)
How can I print logs when the app is being opened from closed state?
It sounds like you're describing a few different things here.
First, you won't be able to see any NSLog notification messages if/after the app is fully terminated, until the app is launched by some mechanism.
If you want to print logs when the app is being opened from a terminated state, you can do something like the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
NSLog(#"%s", __PRETTY_FUNCTION__); // print the function name
// Process remote notifications
NSDictionary *remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotification) {
[self processNotification:remoteNotification];
}
}
- (void)processNotification:(NSDictionary *)payload {
NSString *someString = [payload objectForKey:#"someStringKey"];
// do something!
}

Press a button on apple watch to call a function in parent app

I have never tried creating an app with the watchKit extension and I am running into some problems. All I am trying to do is hit a button on the apple watch and then it will call a function within the parent iOS app. Here is what I have tried so far:
watch:
- (IBAction)buttonPressedWK {
[InterfaceController openParentApplication:#{ #"command": #"foo" }
reply:^(NSDictionary *replyInfo, NSError *error) {
}];
}
Phone app:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply
{
//Code to call function
[self thisFunc];
}
Am I missing something or is there another way I should go about this?

The UIApplicationDelegate in the iPhone App never called reply

I am trying to launch my iPhone app from watch simulator using the below code :
WKInterfaceController subclass
[WKInterfaceController openParentApplication:[NSDictionary dictionaryWithObject:#"red" forKey:#"color"] reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"replyInfo %#",replyInfo);
NSLog(#"Error: %#",error);
}];
AppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply
{
NSLog(#"appdelegate handleWatchKitExtensionRequest");
NSLog(#"NSDictionary: %#",userInfo);
NSLog(#"replyInfo: %#",replyInfo);
}
The error I am getting is :
Error: Error Domain=com.apple.watchkit.errors Code=2 "The
UIApplicationDelegate in the iPhone App never called reply() in
-[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"
UserInfo=0x7f8603227730 {NSLocalizedDescription=The
UIApplicationDelegate in the iPhone App never called reply() in
-[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}
You need to call the reply block, even if you return nil. The following will resolve your error:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply
{
NSLog(#"appdelegate handleWatchKitExtensionRequest");
NSLog(#"NSDictionary: %#",userInfo);
NSLog(#"replyInfo: %#",replyInfo);
reply(nil);
}
See the Apple documentation for further information. You can also return an NSDictionary reply(myNSDictionary); with whatever information it would be useful to return to your Watchkit extension, although the dictionary can only contain information that can be serializable to a property list file, so for instance you can pass strings but you can't just pass a dictionary containing references to instances of your custom classes without packaging them up as NSData first.
Aside from just not calling the reply block, this can happen for at least a couple reasons:
Your iPhone app crashed while it was processing the request and therefore was never able to call the reply block. Check that you are not accidentally putting nil into an NSMutableDictionary, as that will cause a crash.
You are trying to put something that can't be serialized into a plist file into the replyInfo dictionary (hat tip to #duncan-babbage). If you need to pass an NSAttributedString or your custom object, make sure it conforms to NSCoding and do this:
On the phone side build your reply dictionary:
NSMutableDictionary *reply = [NSMutableDictionary new];
MyCustomObject *myObject = <something you need to send>;
reply[#"myKey"] = [NSKeyedArchiver archivedDataWithRootObject: myObject];
NSAttributedString *myString = <some attributed string>;
reply[#"otherKey"] = [NSKeyedArchiver archivedDataWithRootObject: myString];
And unpack it back on the watch side:
NSData *objectData = replyInfo[#"myKey"];
MyCustomObject *myObject = [NSKeyedUnarchiver unarchiveObjectWithData: objectData];
NSData *stringData = replyInfo[#"otherKey"];
NSAttributedString *myString = [NSKeyedUnarchiver unarchiveObjectWithData: stringData];
I would like to add that it is important to start a background task in handleWatchKitExtensionRequest as specified in the documentation. This ensures that the main app on the iPhone is not suspended before it can send its reply. (Not initiating a background task does not cause a problem in the simulator or when the iPhone app is active. However, it causes a problem when the iPhone app is inactive.)
Code in the app delegate of the main app on iPhone:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void ( ^)( NSDictionary * ))reply
{
__block UIBackgroundTaskIdentifier watchKitHandler;
watchKitHandler = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"backgroundTask"
expirationHandler:^{
watchKitHandler = UIBackgroundTaskInvalid;
}];
if ( [[userInfo objectForKey:#"request"] isEqualToString:#"getData"] )
{
// get data
// ...
reply( data );
}
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC * 1 ), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
[[UIApplication sharedApplication] endBackgroundTask:watchKitHandler];
} );
}

cannot receive push notification when app is not running

I've created an iphone app with push notification feature. The server works well and I could receive the notification when app is running in foreground. The function
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
is called then notification arrives.
However, when I press the home key, brought the app into background or kill it in the task bar. I cannot receive any notification, neither in the notification area or any popup.
Anyone knows the reason? Thanks
You can use...
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
if (launchOptions != nil) {
NSMutableDictionary *dic = [launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSMutableDictionary *dicItem = [dic objectForKey:#"aps"];
NSString *itemNotification = [dicItem objectForKey:#"alert"];
}else if (launchOptions == nil){
NSLog(#"launch ,,, nil");
}
...//code something
}
itemNotification is an item in Notification barge. Have fun!!

Resources