I run this in my View Controller:
NSString *indexV = [NSString stringWithFormat:#"%d", index];
[[NSUbiquitousKeyValueStore defaultStore] setString:indexV forKey:#"index"];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
indexV = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:#"index"];
NSLog(#"index V: %#", indexV);
Then quit the app.
In my AppDelegate I have:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
NSString *indexV = [[NSUbiquitousKeyValueStore defaultStore] stringForKey:#"index"];
NSLog(#"indexV: %#", indexV);
NSDictionary *d = #{#"index" : indexV};
reply(d);
In my InterfaceController I have:
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
[self sync];
- (void)sync {
[WKInterfaceController openParentApplication:#{#"give" : #"give"} reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"index: %#", replyInfo[#"index"]);
if (replyInfo[#"index"]) {
index = [replyInfo[#"index"] intValue];
In the iPhone app, I see the log where index is being set to an integer > 0.
Why is this string not synchronizing between apps?
I am able to send arrays ok.
Yes I have app groups configured, which is how - (void)application:(UIApplication *)application handleWatchKitExtensionRequest: .. will respond at all.
But when I run the Apple Watch app, index always comes through as #"0".
Related
I need to implement a ride booking feature via Siri in an application. I managed to add extension to app and now I'm able to communicate with Siri, but I didn't succeed in launching app to continue ride booking process within the application.
So here comes the question, what should be done for launching app from INRequestRideIntent extension?
Thanks in advance!
RideIntentHandler.m
- (void)confirmRequestRide:(INRequestRideIntent *)intent completion:(void (^)(INRequestRideIntentResponse * _Nonnull))completion {
INRideOption *rideOption = [[INRideOption alloc] initWithName:#"blah" estimatedPickupDate:[NSDate dateWithTimeIntervalSinceNow:300]];
rideOption.userActivityForBookingInApplication = [[NSUserActivity alloc] initWithActivityType:#"blahblah"];
INRideStatus *rideStatus = [[INRideStatus alloc] init];
rideStatus.rideOption = rideOption;
rideStatus.estimatedPickupDate = [NSDate dateWithTimeIntervalSinceNow:300];
rideStatus.rideIdentifier = [NSUUID UUID].UUIDString;
INRequestRideIntentResponse *response = [[INRequestRideIntentResponse alloc] initWithCode:INRequestRideIntentResponseCodeSuccess userActivity:nil];
response.rideStatus = rideStatus;
completion(response);
}
AppDelegate.m
- (void)application:(UIApplication *)application handleIntent:(INIntent *)intent completionHandler:(void (^)(INIntentResponse * _Nonnull))completionHandler {
[Utils showCommonAlertWithMessage:#"blah"];
if ([intent isKindOfClass:[INRequestRideIntent class]]) {
[Utils showCommonAlertWithMessage:#"blah"];
}
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
[Utils showCommonAlertWithMessage:#"blah"];
if ([userActivity.interaction.intent isKindOfClass:[INRequestRideIntent class]]) {
NSLog(#"%#", userActivity.userInfo);
[Utils showCommonAlertWithMessage:#"blah"];
}
return YES;
}
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
[Utils showCommonAlertWithMessage:#"blah"];
}
I suppose that this is duplicate but I can not figure it out.
I have to call other app from my iOS app using openUrl method. After finishing its work the other app must return to my app using the same method. I figure out how to call the other App and its open my App too. My problem is how to intercept the return to my App. I need to check the value from query string.
I find that method handleOpenURL intercepts return and I can handle my query string.
And here I am stuck - how to use that info inside my ViewController? I set breakpoint in viewDidLoad but it was not hit. Which method I have to use?
EDIT:
My Code is (inside AppDelegate):
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"url recieved: %#", url);
NSLog(#"query string: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"url path: %#", [url path]);
NSDictionary *dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
return YES;
}
- (NSDictionary *)parseQueryString:(NSString *)query {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:6];
NSArray *pairs = [query componentsSeparatedByString:#"&"];
for (NSString *pair in pairs) {
NSArray *elements = [pair componentsSeparatedByString:#"="];
NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *val = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[dict setObject:val forKey:key];
}
return dict;
}
Which works fine.
Inside my ViewController (VC):
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setNeedsStatusBarAppearanceUpdate];
// Instantiate App singleton
singApp = [PESsingApplication sharedInstance];
#try {
// Localize resources using currently saved setting for language
[self setLocalizedResources];
// Init visual buttons
[self baseInit];
// Add code for keyboard management
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardHide:)
name:UIKeyboardWillHideNotification
object:nil];
CGRect screenRect = [[UIScreen mainScreen] bounds];
_screenHeight = screenRect.size.height;
_screenWidth = screenRect.size.width;
}
#catch (NSException *exception) {
[self throwUnknownException:exception];
}
}
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
My url:
URL identifier: xx.mydomain.MyUrlScheme
URL shcemes: MyUrlScheme
I have breakpoints inside my VC (on each of the method shown above).
I use following string to call other app: #"otherApp://openApp?param1=value1&callbackUrl=MyUrlScheme";
They call me from the otherApp using callbackUrl param.
You need to make your own custom URL, please look below
How to implement Custom URL Scheme
Defining your app's custom URL scheme is all done in the Info.plist file. Click on the last line in the file and then click the "+" sign off to the right to add a new line. Select URL Types for the new item. Once that's added, click the grey arrow next to "URL Types" to show "Item 0". Set your URL identifier to a unique string - something like com.yourcompany.yourappname.
After you've set the URL identifier, select that line and click the "+" sign again, and add a new item for URL Schemes. Then click the grey arrow next to "URL Schemes" to reveal "Item 0". Set the value for Item 0 to be your URL scheme name.
Handling Custom URL Calls
In order for your app to respond when it receives a custom URL call, you must implement the application:handleOpenURL method in the application delegate class:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
// your code
}
Parsing the Custom URL
There are several parts to a URL:
scheme://host/path?query
The parts to the URL can be retrieved through the NSURL object that is passed into the application:handleOpenURL method. If you have a fairly simple URL naming scheme and want to allow access to specific pages/keys, you can just use the host name:
Custom URL Value of [url host]:
myapp://page1 page1
myapp://page2 page2
myapp://otherPage otherPage
To pass data into your app, you'll want to use the query string. Here's a simple method for parsing the query string from the url:
- (NSDictionary *)parseQueryString:(NSString *)query {
NSMutableDictionary *dict = [[[NSMutableDictionary alloc] initWithCapacity:6] autorelease];
NSArray *pairs = [query componentsSeparatedByString:#"&"];
for (NSString *pair in pairs) {
NSArray *elements = [pair componentsSeparatedByString:#"="];
NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *val = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[dict setObject:val forKey:key];
}
return dict;
}
Testing The Custom URL
You can easily test your URL scheme in the simulator. Just add a test button to one of your views, and implement the IBAction method for it as follows:
- (IBAction)getTest:(id)sender {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"myappscheme://test_page/one?token=12345&domain=foo.com"]];
}
Then in your app delegate, implement the application:handleOpenURL method:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"url recieved: %#", url);
NSLog(#"query string: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"url path: %#", [url path]);
NSDictionary *dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
return YES;
}
Finally if you are looking method to receive your data anywhere you can use this two scenario.
You can simple use Local notification or NSUserDefault
NSUserDefault
- (BOOL)application:(UIApplication *)application handleopenURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSUserDefaults *userDefaults=[[NSUserDefaults alloc] init];
[userDefaults synchronize];
NSString *status = [defaults stringForKey:#"any status"];
}
Local notification
- (BOOL)application:(UIApplication *)application handleopenURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:VAL, #"value", nil];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}
If your viewDidLoad is not called perfectly try in viewWillAppear or viewDidAppear method.
For example purpose:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSDictionary *dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
// add dictionary to standardUserDefaults for saving purpose, like
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:#"DicKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
// add code for navigation/present view controller
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main"
bundle: nil];
YourViewController *yourController = (YourViewController *)[mainStoryboard
instantiateViewControllerWithIdentifier:#"YourViewControllerID"];
self.window.rootViewController = yourController;
return YES;
}
for retrieve
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableDictionary *mutableRetrievedDictionary = [[[NSUserDefaults standardUserDefaults] objectForKey:#"DicKey"] mutableCopy];
// here parse the dictionary and do your work here, when your works is over
// remove the key of standardUserDefaults
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"DicKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Store the status from other app in NSUserdefaults, when the ViewController of your app launches fetch the status into a NSString from NSUserdefaults and rise it as an alert.
Call the handleopenURL in appdelegate
- (BOOL)application:(UIApplication *)application handleopenURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSUserDefaults *defaults=[[NSUserDefaults alloc] init];
[defaults synchronize];
NSString *status = [defaults stringForKey:#"status string from other app"];
}
as apple suggested use Handoff in Glance .
I wants to call web API in Glance Interface , for this I did following things
- (void)awakeWithContext:(id)context
{
[super awakeWithContext:context];
[self CreateUaerActivity];
}
-(void)CreateUaerActivity
{
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:#"com.xxx.xxx.glance"];
activity.title = #"Glance";
activity.delegate=self;
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:kUserLoginWatchKit,kRequestTypeWatchKit, nil];
activity.userInfo = dict;
self.userActivity = activity;
[self.userActivity becomeCurrent];
}
- (void)willActivate
{
[super willActivate];
[NSTimer scheduledTimerWithTimeInterval:120 target:self selector:#selector(doSomething) userInfo:nil repeats:YES];
}
-(void)doSomething
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:kUserLoginWatchKit,kRequestTypeWatchKit, nil];
[super updateUserActivity:#"com.xxx.xxx.glance" userInfo:dict webpageURL:nil];
}
-(void)handleUserActivity:(NSDictionary *)userInfo
{
//displaying data
}
and in AppDelegate.m file -
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
NSLog(#"Handoff dictionary: %#", userActivity.userInfo);
NSString *requestType = userActivity.userInfo[kRequestTypeWatchKit];
if ([requestType isEqual: kGlanceDataWatchKit])
{
//calling web API to get Data
}
return YES;
}
I found AppDelegate never called continueUserActivity method to return something to Glance interface.
please guide me how to call API through Glance Interface.
I'm not sure if this is what you want, but if you want to call an web Api i suggest yout to do it like this :
in the GlanceInterfaceController :
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:#"getSomething" forKey:#"action"];
[MainInterfaceController openParentApplication:dictionary reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"Reply received by Watch app: %#", replyInfo); // the reply from the appDelegate...
}
in your parent's app Delegate :
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
NSLog(#"Request received by iOS app");
if( [userInfo objectForKey:#"action"] isEqualToString:#"getSomething"] ){
//call you're Web API
//send the reponse to you're glance :
reply(DictResponse);// some Dictionary from your web API...
}
*****EDIT*****
i've been issued the same issue, one easy fix is to begin an background task, from :
fiveminutewatchkit
Here's the way :
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
// Temporary fix, I hope.
// --------------------
__block UIBackgroundTaskIdentifier bogusWorkaroundTask;
bogusWorkaroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bogusWorkaroundTask];
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] endBackgroundTask:bogusWorkaroundTask];
});
// --------------------
__block UIBackgroundTaskIdentifier realBackgroundTask;
realBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
reply(nil);
[[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
}];
// Kick off a network request, heavy processing work, etc.
// Return any data you need to, obviously.
reply(nil);
[[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
}
in fact iOS kill your parent's app before you can retrieve data... this (not very clean solution) prevent you're app to be killed... and let you the time to retrieve infos...
******END EDIT******
My application is webview.
The normal it load page http://staging.nhomxe.vn.
But when server send a notify, and attachmented a link. Webview will open this link.
My application activity normal. Then, i add Navigation Controller to ViewControler.
When Server send notify, my webview notify error at [vc.webView loadRequest:urlRequest]; , and application auto exit.
My code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound)];
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (localNotif) {
// launched from notification
NSLog(#"Co notify!!!");
NSString *message = [localNotif valueForKey:#"link"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:message forKey:#"LINK"];
} else {
// from the springboard
NSLog(#"Khong co notify!!!");
}
return YES;
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(#"My token: %#",deviceToken);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:deviceToken forKey:#"TOKEN"];
}
- (void) application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(#"Error: %#",error);
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)application:(UIApplication*)application didReceiveRemoteNotification:
(NSDictionary*)userInfo
{
NSURL *url ;
NSLog(#"Received notification: %#", userInfo);
NSDictionary *data = [ userInfo objectForKey:#"aps"];
for(NSString *key in data) {
NSString *info = [data objectForKey:key];
NSLog(#"thong tin nhan dc: %# : %#", key, info);
}
NSString *message = [userInfo valueForKey:#"link"] ;
//NSArray *info = [message componentsSeparatedByString:#"&#"];
//NSString *body = [info objectAtIndex:0];
//NSString *link = [info objectAtIndex:1];
NSLog(#"Thong tin Link: %#",message);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:message forKey:#"LINK"];
ViewController *vc = (ViewController *)self.window.rootViewController;
if(message == NULL)
{
url = [NSURL URLWithString:#"http://staging.nhomxe.vn"];
}else
{
url = [NSURL URLWithString:message];
}
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[vc.webView loadRequest:urlRequest];
[vc.webView3 loadRequest:urlRequest];
}
#end
My error:
2014-08-28 14:51:59.374 NhomXe[30379:907] Thong tin Link: http://staging.nhomxe.vn/org/instance_message/conversation-detail.xhtml?post=13001628&orgId=190000168#vehicletracking
2014-08-28 14:52:14.950 NhomXe[30379:907] -[UINavigationController webView]: unrecognized selector sent to instance 0x1d5c8350
2014-08-28 14:52:14.958 NhomXe[30379:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController webView]: unrecognized selector sent to instance 0x1d5c8350'
*** First throw call stack:
(0x314e22a3 0x3913e97f 0x314e5e07 0x314e4531 0x3143bf68 0xd687f 0x3353d585 0x3353dfa5 0x33f53305 0x314b7173 0x314b7117 0x314b5f99 0x31428ebd 0x31428d49 0x34fa52eb 0x3333e301 0xd6ad9 0x39575b20)
libc++abi.dylib: terminate called throwing an exception
Your rootViewController is returning a UINavigationController which is used to manage a navigation stack, that is represented by an array of view controllers.
To get your view controller try getting the topViewController, for example:
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
ViewController *vc = (ViewController *)navigationController.topViewController;
You can also get a array of all view controller that navigation manages by:
NSLog(#"%#", navigationController.viewControllers);
Update your UI on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
// make sure vc is a weak refernace to avoid retain cycle
[vc.webView loadRequest:urlRequest];
[vc.webView3 loadRequest:urlRequest];
});
I'm receiving notifications using this method:
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadComments" object:nil];
}
That trigger this method:
- (void) reloadComments:(NSNotification *)notification{
NSDictionary* dict = [notification.userInfo objectForKey:#"commentNotification"];
NSString* video_id = [[[dict objectForKey:#"aps"] objectForKey:#"custom"] objectForKey:#"data"];
NSData* cData = [video_id dataUsingEncoding:NSUTF8StringEncoding];
NSError *errorJson2;
NSMutableDictionary *response = [NSJSONSerialization JSONObjectWithData:cData options:kNilOptions error:&errorJson2];
int number = [[commentsDictionary objectForKey:[NSString stringWithFormat:#"%#", [response objectForKey:#"video_id"]]] intValue];
number += 1;
[commentsDictionary setObject:[NSNumber numberWithInt:number] forKey:[NSString stringWithFormat:#"%#", [response objectForKey:#"video_id"]]];
}
I'm parsing the result and incrementing the number. This works correctly when i launch the app, in my device, through XCode. If i send 5 push notifications the number is 5.
If i do the same procedure without launching the app through XCode, the number is not correctly incremented.
Anyone has any experience with this and can point me in the right direction?
I added the completionHandler below and it started working.
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[self parsePushNotifications:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}