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******
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 am trying to implement App-to-App calling with Sinch in my IOS app. I have implemented Pushkit in my iOS app with Sinch but the push notification is not working when the app is in background.
I have two questions.
Do I need another web service to send push notification to my app for incoming app separately or Sinch handles it itself.
If it does handle itself then what am I missing in my code.
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[self handleLocalNotification:[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]];
self.push = [Sinch managedPushWithAPSEnvironment:SINAPSEnvironmentAutomatic];
self.push.delegate = self;
[self.push setDesiredPushTypeAutomatically];
[self.push registerUserNotificationSettings];
return YES;
}
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options {
return [[GIDSignIn sharedInstance] handleURL:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]];
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[GIDSignIn sharedInstance] handleURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (id<SINClient>)client {
return _sinchClient;
}
-(void)clientDidFail:(id<SINClient>)client error:(NSError *)error{
NSLog(#"fail");
}
-(void)clientDidStart:(id<SINClient>)client{
NSLog(#"Start");
[self voipRegistration];
}
- (void)client:(id<SINClient>)client
logMessage:(NSString *)message
area:(NSString *)area
severity:(SINLogSeverity)severity
timestamp:(NSDate *)timestamp {
// If you want all messages remove the if statement
if (severity == SINLogSeverityCritical) {
NSLog(#"%#", message);
}
}
- (void)initSinchClientWithUserId:(NSString *)userId {
if (!_sinchClient) {
_sinchClient = [Sinch clientWithApplicationKey:#"<my-key>"
applicationSecret:#"<my-secret>"
environmentHost:#"sandbox.sinch.com"
userId:userId];
_sinchClient.delegate = self;
[_sinchClient setSupportCalling:YES];
[_sinchClient startListeningOnActiveConnection];
[_sinchClient enableManagedPushNotifications];
[_sinchClient start];
}
}
- (void)handleLocalNotification:(UILocalNotification *)notification {
if (notification) {
id<SINNotificationResult> result = [self.sinchClient relayLocalNotification:notification];
if ([result isCall] && [[result callResult] isTimedOut]) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Missed call"
message:[NSString stringWithFormat:#"Missed call from %#", [[result callResult] remoteUserId]]
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[alert show];
}
}
}
-(void)voipRegistration
{
PKPushRegistry* voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
voipRegistry.delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
-(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
[_sinchClient registerPushNotificationData:credentials.token];
}
-(void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type{
NSLog(#"invalidated");
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType: (NSString *)type
{
//notify
NSDictionary* dic = payload.dictionaryPayload;
NSString* sinchinfo = [dic objectForKey:#"sin"];
UILocalNotification* notif = [[UILocalNotification alloc] init];
notif.alertBody = #"incoming call";
[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
if (sinchinfo == nil)
return;
dispatch_async(dispatch_get_main_queue(), ^{
[_sinchClient relayRemotePushNotificationPayload:sinchinfo];
});
}
If you integrated Pushkit and Sinch then push notification may can't catch on PushKit's delegate function - didReceiveIncomingPushWithPayload. But you can get push notification on SINManagedPushDelegate's function - didReceiveIncomingPushWithPayload.
Push notification is not coming but you can get incoming call event on there when app is in background. You can trigger local notification if app is in background to let user incoming call know.
Hope that would be helpful for you.
I'm trying to temporarily resume (un-suspend?) my replication tasks in couchbase lite iOS when my app receives a push notification. I've tried the following in my AppDelegate.m
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"notification-body => %#", notification);
NSMutableArray *toSuspend = [NSMutableArray array];
CBLManager *manager = [CBLManager sharedInstance];
for (NSString* name in [manager allDatabaseNames]) {
CBLDatabase* db = [manager databaseNamed:name error:nil];
NSArray* replications = [db allReplications];
for(CBLReplication* rep in replications) {
if(rep.suspended) {
[rep setSuspended: NO];
[toSuspend addObject: rep];
}
}
}
[RCTPushNotificationManager didReceiveRemoteNotification:notification];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"notification-complete => %#", notification);
for(CBLReplication* rep in toSuspend) {
[rep setSuspended: YES];
}
completionHandler(UIBackgroundFetchResultNewData);
});
}
But the list of replicators is always empty even though I have 2 running. What am I doing wrong?
I am not sure where exactly my question fit. Here is the issue :
I want the push notifications to be registered on application start. For which I am registering in AppDelegate didFinishLaunchingWithOptions.
sem = dispatch_semaphore_create(0);
[manager registerForPushNotifications];
dispatch_semaphore_signal(sem);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
If I don't use GCD, return YES; of didFinishLaunchingWithOptions is called first and in that case, my my service method, which want to call from didRegisterForRemoteNotificationsWithDeviceToken for sending device token is not called.
// system push notification registration success callback, delegate to pushManager
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#"<>"]];
token = [token stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"content---%#", token);
[[NSUserDefaults standardUserDefaults]setObject:token forKey:#"deviceToken"];
[self registerForPushWooshNotification];
[[PushNotificationManager pushManager] handlePushRegistration:deviceToken];
}
-(void)registerForPushWooshNotification
{
NSDictionary *params = #{#"TokenId": [[NSUserDefaults standardUserDefaults]objectForKey:#"deviceToken"]
};
[_sharedHandler.requestManager POST:TGURL_PUSHWOOSH_NOTIFICATION
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError *e;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:[operation.responseString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&e];
NSLog(#"------ Registered for Pushwoosh ------");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
}
But as I have implemented the GCD, didRegisterForRemoteNotificationsWithDeviceToken never gets called.
Summary :
1. I have to register application on app start.
2. Web Service needs to be called on app start.
3. If GCD not used : return YES; is called first, and didRegisterForRemoteNotificationsWithDeviceToken gets called after delay.
4. If GCD used : didRegisterForRemoteNotificationsWithDeviceToken is never called.
I had searched for didFinishLaunchingWithOptions wait and didRegisterForRemoteNotificationsWithDeviceToken called after delay on google before posting this question but no success.
The problem here is that by waiting on the semaphore, you are blocking the main thread, and the callback you are expecting to happen later will be delivered on the main thread. Since you're blocking the main thread, the callback will never happen. If you want your app to not do anything until that callback is received, you have to set that up another way.
The solution that would be philosophically closest to what you have now would be to spin the main run loop while waiting waiting for the callback, but there are a number of different ways to do it, and that's probably not the way I would choose. That said, if you wanted to do it that way, it might look something like this:
#implementation AppDelegate
{
BOOL didRegisterCalled;
NSData* token;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application registerForRemoteNotifications];
NSLog(#"registerForRemoteNotifications called. waiting for callback.");
while (!didRegisterCalled)
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
}
NSLog(#"Register call back happened, and execution resumed");
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
didRegisterCalled = YES;
token = [deviceToken copy];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
didRegisterCalled = YES;
token = nil;
}
#end
The simplest way would be to split your startup tasks into a separate method and have the callback call that method. (FWIW, this is probably the approach I would choose.) That might look like this:
#implementation AppDelegate
{
BOOL didRegisterCalled;
NSData* token;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"Calling registerForRemoteNotifications and deferring the rest of app startup.");
[application registerForRemoteNotifications];
return YES;
}
- (void)theRestOfTheAppStartupProcess
{
NSLog(#"Finishing app startup now that registration has happened.");
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
token = [deviceToken copy];
if (!didRegisterCalled)
{
didRegisterCalled = YES;
[self theRestOfTheAppStartupProcess];
}
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
token = nil;
if (!didRegisterCalled)
{
didRegisterCalled = YES;
[self theRestOfTheAppStartupProcess];
}
}
#end
Another way might be to set up a dispatch queue, suspend it, and then have the callback resume it. That might look like this:
#implementation AppDelegate
{
dispatch_queue_t appStartupQueue;
NSData* token;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
appStartupQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_suspend(appStartupQueue);
dispatch_set_target_queue(appStartupQueue, dispatch_get_main_queue());
dispatch_async(appStartupQueue, ^{
[self theRestOfTheAppStartupProcess];
});
NSLog(#"Calling registerForRemoteNotifications and deferring the rest of app startup.");
[application registerForRemoteNotifications];
return YES;
}
- (void)theRestOfTheAppStartupProcess
{
NSLog(#"Finishing app startup now that registration has happened.");
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
token = [deviceToken copy];
if (appStartupQueue)
{
dispatch_resume(appStartupQueue);
appStartupQueue = nil;
}
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
token = nil;
if (appStartupQueue)
{
dispatch_resume(appStartupQueue);
appStartupQueue = nil;
}
}
#end
But what you have currently is going to produce a deadlock.
I am using chat api for IOS of quickblox in my project, chat application, now everything is ok... When the user is online, or the application not removed from background he is getting push notifications and messages in chat page.. but when the application is removed from background, he is getting push notifications, but the message doesnt appear in the chat page it's appear only in the push notification... I sent my problem to quickblox support, they gave me a code to replace in my project, now my problem code is here:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
// Override point for customization after application launch.
cManager = [ConnectionManager sharedMannager];
[cManager sendNetworkIdUpdate:[[DataManager sharedCenter]currentnetworkIdentifier]];
[QBSettings setApplicationID:-----];
[QBSettings setAuthorizationKey:#"--------"];
[QBSettings setAuthorizationSecret:#"--------"];
[QBSettings setAccountKey:#"--------"];
[cManager getBlockedList];
if (launchOptions != nil) {
NSLog(#"Launched from push notification");
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
NSLog(#"%#",launchOptions);
[[DataManager sharedCenter]setPushData:[notification valueForKeyPath:#"aps.user_info"]];
// Do something with the notification dictionary
}
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application{
if ([[[DataManager sharedCenter]currentUser]userChatId]){
[QBUsers logOutWithDelegate:[ChatService instance]];
}
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
if ([[DataManager sharedCenter]chat_user] != nil) {
[[QBChat instance] loginWithUser:[[DataManager sharedCenter]chat_user]];
}
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
BOOL isLoggedIntoChat = [[QBChat instance] isLoggedIn];
if (isLoggedIntoChat) {
[[QBChat instance] logout];
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
if ([[UIApplication sharedApplication]applicationIconBadgeNumber]>0){
[UIApplication sharedApplication].applicationIconBadgeNumber=0;
[cManager clearBadge];
}
// 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:.
/*if ([[[DataManager sharedCenter]currentUser]userChatId]){
[QBUsers logOutWithDelegate:[ChatService instance]];
}*/
if ([[LocalStorageService shared]messagesHistory] && [[[DataManager sharedCenter]currentUser]userId]){
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[[LocalStorageService shared]messagesHistory]];
[[[DataManager sharedCenter]defaults]setObject:data forKey:[NSString stringWithFormat:#"%#%#",[[[DataManager sharedCenter]currentUser]userId],kMessageHistoryBlock]];
[[[DataManager sharedCenter]defaults]synchronize];
}
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSString* devToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
NSLog(#"Update Token: %#",devToken);
[cManager sendPushTokenUpdate:devToken];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSLog(#"Opend from push notification");
NSLog(#"%#",userInfo);
UIApplicationState state = [[UIApplication sharedApplication]applicationState];
if (state != UIApplicationStateActive){
if ([userInfo valueForKeyPath:#"aps.user_info"]){
[[DataManager sharedCenter]setPushData:[userInfo valueForKeyPath:#"aps.user_info"]];
[[NSNotificationCenter defaultCenter]postNotificationName:kShowPush object:nil];
}
}
[[NSNotificationCenter defaultCenter]postNotificationName:kUpdateTables object:nil];
[cManager clearBadge];
}
and the problem is still appearing ! , any help please ?
You have to do proper Chat logout. Server should know for sure that your user is offline
First of all, refer to the Chat in background mode documentation http://quickblox.com/developers/SimpleSample-chat_users-ios#Chat_in_background_mode
Try to do chat logout in applicationWillTerminate and applicationDidEnterBackground:
- (void)applicationWillTerminate:(UIApplication *)application
{
[[QBChat instance] logout];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[QBChat instance] logout];
}