I was wondering if there is a good way to send silent push notifications to users using the parse.com services.
By "silent", i mean no actual notification if the user is in the app (I would send normal one if the user was out of the app), no "alert" message, no nothing. Just a discrete function call.
I need this to perform some code while the user is in the app.
I've read in the doc that I can use cloudcode but
Is it best?
How can i do it? there is no other explanation about it.
Is there another way that is more efficient/mobile friendly to call a function remotely without the user noticing.
Should I use obj-C code? cloud code? Can you provide a small example ? (I really just need to call a "refresh" function in my code silently, nothing fancy)
Thanks a lot :)
I do this on for me and it works .
First :
In your project capabilities go to "background" and check "remote notification"
Second :
In your appdelegate be sure to have this method which handle the background (silent) push.
-(void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
//i handle the silent push here with a test on the userinfo's param.
// if content-available = 1 do some stuff
// else
// [PFPush handlePush:userInfo];
}
and finally :
When you set the data in your push you have to add "content-available" = 1 and remove the sound
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
temp, #"alert",
#"Increment", #"badge",
#"", #"sound",
#1, #"content-available",
nil];
or
NSDictionary *data =#{
#"badge": #"Increment",
#"alert": temp,
#"sound": #"",
#"content-available" :#1
};
You can use cloud code functionality for such type of scenarios.
I'm not that much aware of parse cloud functionality. Here's the sample cloud code of what you expect.
This is how you define a cloud function
Parse.Cloud.define("SomeFunction", function(request, response) {
//Write queries as you need
});
Then call this cloud function as follows
[PFCloud callFunctionInBackground:#"SomeFunction" withParameters:parameters block:^(id object, NSError *error) {
//Add this function in some method & call the method wherever you needed, suppose if you need to update your app which has SwipingSideMenu like functionalities, for each time you click on the menu or side button call this cloud function. Thats it.
}];
Also you can refer to https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
The aps dictionary can also contain the content-available property. The content-available property with a value of 1 lets the remote notification act as a “silent” notification. When a silent notification arrives, iOS wakes up your app in the background so that you can get new data from your server or do background information processing. Users aren’t told about the new or changed information that results from a silent notification, but they can find out about it the next time they open your app.
Related
I'm setting up registrations for notifications of iCloud changes.
Say a new device is added to the icloud account, I'm just wondering how that device will get the private database records.
Do I need to do a one off query?
I'm hoping that notifications will be used at all other times.
Let's start with some relevant characteristics of subscription notifications:
First: Subscription Notifications are specific to a user + device pair. If I install you app on my phone, I start getting notifications. I won't get the notifications on another device until I install the app there, too.
Second: Notifications are unreliable. Apple docs are quite clear that they do not guarantee delivery. When you receive a notification, there could have been several prior notifications. Thus, Apple offer's two mechanisms to track which notifications you've seen:
Read/unread status: you can mark notifs as read. Apple's docs contradict themselves about what this actually does. This page says
If you mark one or more notifications as read using a CKMarkNotificationsReadOperation object, those notifications are not returned, even if you specify nil for previousServerChangeToken.
However, this isn't true. The fetch operation clearly returns both read and unread notifications. WWDC 2014 Video 231 (Advanced Cloudkit) contradicts the documentation page, explaining that unread tokens are always returned as well as read tokens so multiple devices can sync up. The video gives a specific example that shows the benefits of this behavior. This behavior is also documented on SO: CKFetchNotificationChangesOperation returning old notifications
change token: each fetch operation will return a change token that you can cache. If you pass the token to a fetch, the fetch will only return tokens from that point, whether read or unread.
At first glance, it would seem that Apple is providing for the behavior you want: install the app on one device, start processing notifications, install the app on a second device, and fetch all those prior notifications in order to catch up.
Unfortunately, as I've documented in CKFetchNotificationChangesOperation: why are READ notifications all nil?, any time I fetch notifications, the ones previously marked as "read" all have nil contents. All the info in the read notifications is lost.
In my scenario, I chose to:
Always fetch the latest record(s) at startup
Fetch notifications using the previously saved change token (if it exists)
Process the new notifications
Mark the notifications as read
save the latest change token for use on the next fetch
For your scenario, you could try:
Fetch notifications using the previously saved change token (if it exists)
process the notifications (DO NOT MARK THEM AS READ)
save the latest change token for use on the next fetch
Your first device will ignore old notifications on each subsequent fetch because you're starting each fetch from the change token point. Your second device will start with a nil change token on the first execution, and thus pick up all of the old notifications.
One word of caution: even though aforementioned WWDC video clearly says Apple keeps all the old notifications, I have found no documentation that says how long they hold this info. It may be forever, it may not.
updated with notification fetch example
Here's how I'm fetching notifications, marking them read, and caching the change token:
#property CKServerChangeToken *notificationServerChangeToken;
Then...
-(void)checkForUnreadNotifications
{
//check for unread cloudkit messages
CKFetchNotificationChangesOperation *op = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:_notificationServerChangeToken];
op.notificationChangedBlock = ^(CKNotification *notification)
{
//this fires for each received notification. Take action as needed.
};
//maintain a pointer to the op. We will need to look at a property on the
//op from within the completion block. Use __weak to prevent retain problems
__weak CKFetchNotificationChangesOperation *operationLocal = op;
op.fetchNotificationChangesCompletionBlock = ^(CKServerChangeToken *newServerChangeToken, NSError *opError)
{
//this fires once, at the end, after all notifications have been returned.
//this is where I mark the notifications as read, for example. I've
//omitted that step because it probably doesn't fit your scenario.
//update the change token so we know where we left off
[self setNotificationServerChangeToken:newServerChangeToken];
if (operationLocal.moreComing)
{
//more notifications are waiting, recursively keep reading
[self checkForUnreadNotifications];
return;
}
};
[[CKContainer defaultContainer] addOperation:op];
}
To set and retrieve the cached change token from the user defaults, I use the following two functions:
-(void)setNotificationServerChangeToken:(CKServerChangeToken *)newServerChangeToken
{
//update the change token so we know where we left off
_notificationServerChangeToken = newServerChangeToken;
NSData *encodedServerChangeToken = [NSKeyedArchiver archivedDataWithRootObject:newServerChangeToken];
NSUserDefaults *userSettings = [NSUserDefaults standardUserDefaults];
[userSettings setObject:encodedServerChangeToken forKey:UD_KEY_NOTIFICATION_TOKEN_CKSERVERCHANGETOKEN_PROD];
//Note, the development and production cloudkit environments have separate change tokens. Depending on your needs, you may need to save both.
}
and...
-(void)getNotificationServerChangeToken
{
NSUserDefaults *userSettings = [NSUserDefaults standardUserDefaults];
NSData *encodedServerChangeToken = [userSettings objectForKey:UD_KEY_NOTIFICATION_TOKEN_CKSERVERCHANGETOKEN_PROD];
_notificationServerChangeToken = [NSKeyedUnarchiver unarchiveObjectWithData:encodedServerChangeToken];
}
I'm successfully getting push notifications for chats which are send by QuickBlox it self. However there's certain cases where I'm sending push notifications to other users in code too. At this point, I lost my self in sea !
Another user who's currently logged-out, so the app is showing 5 in badge counter (for unread chats).. then if I send him a push via code it should increase to 6 ... but its not updating. How do I pass 6 in QBMPushMessageBadgeKey with push I'm sending in code.
Please help me in figuring this out !! This will gonna a big issue in QuickBlox.
For sending push notifications you can use
+ (QBRequest *)createEvent:(QBMEvent *)event successBlock:(void (^)(QBResponse *, NSArray *))successBlock
errorBlock:(QBRequestErrorBlock)errorBlock;
and the simplest way is to use:
+ (QBRequest *)sendPush:(QBMPushMessage *)pushMessage toUsers:(NSString *)usersIDs successBlock:(void(^)(QBResponse *response, QBMEvent *event))successBlock errorBlock:(void (^)(QBError *error))errorBlock
My Objective C app on iOS 7 gets location updates in the background from either the startUpdatingsignificantLocationChanges or startUpdatingLocation delegate (which one depends on the mode that the app is in, but I don't think it matters).
In the delegate, I gather the location info, write it to a dictionary, and then write the dictionary to a Firebase.
// this code is in the location update delegate routine
// the code that gathers the various elements that go into the dictionary
// are omitted for clarity, I don't think that they matter
// we may be running in the background on iOS 7 when we are called!
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[[NSNumber numberWithFloat:newLocation.coordinate.latitude] stringValue], #"Latitude",
[[NSNumber numberWithFloat:newLocation.coordinate.longitude] stringValue], #"Longitude",
[[NSNumber numberWithFloat:newLocation.horizontalAccuracy] stringValue], #"Accuracy",
formattedDateString, #"TimeNow",
[dateFormatter stringFromDate:newLocation.timestamp], #"TimeStamp",
[[NSNumber numberWithDouble:interval] stringValue], #"Date",
self.mode, #"Mode",
nil];
// Write it once to CurrentLocation
[ref setValue:dictionary];
// yes, I know this is clumsy
fbTmp = [NSMutableString stringWithString: fbRoot];
[fbTmp appendString : #"/locationHistory"];
ref = [[Firebase alloc] initWithUrl:fbTmp];
// Now write it again to the locationHistory list
ref = [ref childByAutoId];
[ref setValue:dictionary];
Sometimes it works, sometimes it doesn't (i.e. in the same run of the app, sometimes the location gets written to the Firebase successfully as expected, and sometimes it doesn't. There isn't any obvious rhyme or reason to when it seems to work and when it doesn't).
I suspect that the issue is that the Firebase write is not completing successfully in background mode, but I'm not sure. I am very new to iOS and Objective C and Firebase.
My app is marked in its Capabilities as requiring background services for Location updates and Background fetch (the latter my random attempt to fix this problem, the former I know that I need).
My suspicion is that I need to tell the OS that I need time to complete the write with a backkgroundTask, and then terminate the background task in the completion block of the firebase write - has anyone verified that that will work when running in background mode?
If so, do I just need to do that in the second write (assuming that they are completed in order), or in both (with a counter that I count down as each write completes)?
Any hints most appreciated.
Yes, you need to finish your task in background. It says so in the Apple Documentation:
If your app is in the middle of a task and needs a little extra time to complete that task, it can call the beginBackgroundTaskWithName:expirationHandler: or beginBackgroundTaskWithExpirationHandler: method of the UIApplication object to request some additional execution time. Calling either of these methods delays the suspension of your app temporarily, giving it a little extra time to finish its work. Upon completion of that work, your app must call the endBackgroundTask: method to let the system know that it is finished and can be suspended.
Just put your code in a background task, give it maximum time (3 minutes I think).
Read up on Apple app lifecycle and everything should clear up for you for future reference.
I'm trying to receive a message when the local store changes to the iCloud Store.
This is a critical event. So my use case is a new device receiving the iCloud Store after starting with an empty store. I would like to notify the view to update with the received content.
I initialize my managed object context like this:
[self.managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:self.storeURL
options:#{ NSPersistentStoreUbiquitousContentNameKey : #"iCloudStore", [NSNumber numberWithBool:YES]: NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES]:NSInferMappingModelAutomaticallyOption}
error:&error];
My next step is to get the following notifications:
NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
[dc addObserver:self
selector:#selector(storesWillChange:)
name:NSPersistentStoreCoordinatorStoresWillChangeNotification
object:psc];
[dc addObserver:self
selector:#selector(storesDidChange:)
name:NSPersistentStoreCoordinatorStoresDidChangeNotification
object:psc];
[dc addObserver:self
selector:#selector(persistentStoreDidImportUbiquitousContentChanges:)
name:NSPersistentStoreDidImportUbiquitousContentChangesNotification
object:psc];
I thought that implementing the update ui in the name:NSPersistentStoreCoordinatorStoresDidChangeNotification should do the thing. But somehow it appears that this is what I intended to do.
Edit:
With the related post in the accepted answer I could solve my issue
I check the notifications userdict in
like this: storesDidChange:
NSNumber *storeVal = [note.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey];
if (storeVal.integerValue == NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted) {
//you are now in sync with iCloud
NSLog(#"On iCloud Store now");
[self.delegate storeHasChanged];
}
See link below for a description of handling Core Data store change events. NOTE that the first storesDidChange notification is identical regardless of the state of your store. However if this is the first time you are creating the store AND there is an iCloud store already you will get another storesDidChange notification once the existing iCloud store initial import has been completed.
The problem is you don't know what the situation is before it happens UNLESS you know you are creating a new store and a store already exists in iCloud.
Sadly as noted in my explanation there is no real switch between a local store and an iCloud store - however Core Data does somehow import the sideLoad store at which point you get the transition type 4 notification (second storesDidChange).
Also be aware that if your store needed to be upgraded to a new model version you also get a whole bunch of storesDidChange notifications...
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/sample-apps-explanations/handling-icloud-account-transitions/
You may also want to check out how the sample apps do what you are trying to do at the same link
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/
My iOS app is crashing when it receives a push notification message while running. I'm using the sandbox APNS environment, and using Amazon SNS to send the APNS messages.
When debugging, I set a breakpoint on the first line of the following code snippet:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSDictionary *apnsPayload = [NSDictionary dictionaryWithDictionary:userInfo];
When the app receives a push notification, it hits the breakpoint and lets me debug; at this point I can see that userInfo is non-nil and contains the expected dictionary.
However, when I step through the code, the app crashes with EXC_BAD_ACCESS at the very next line—the assignment to apnsPayload. It seems like userInfo might be getting deallocated prematurely, but I'm not sure why, or more importantly how to change this.
I don't think it makes sense to turn the NSDictionary into another NSDictionary with [NSDictionary dictionaryWithDictionary:] ... also NSDictionary can be non-nil but contain 0 key entries (an empty dictionary).
Perhaps you want: NSDictionary *apnsPayload = [userInfo objectForKey: #"alert"]; ?
See: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
Well, I've still no idea why this is happening, but it stops when I NSLog() the userInfo argument first. Adding the following line as the first line of the function prevents the crash:
NSLog(#"Received APNS with userInfo %#", userInfo);
I can then assign using - [userInfo objectForKey:] without causing a crash. (To be clear, attempting this same assignment without the prior NSLog() results in the EXC_BAD_ACCESS crash.)