Why does my iOS app crash when receiving a push notification? - ios

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.)

Related

WatchKit extension crash: "Program ended with exit code: 0"

For people wanting to reply quickly without reading the post: I am not hitting any memory limits. Read the whole post for details.
My WatchKit extension cannot properly function without the user first being "onboarded" through the phone app. Onboarding is where the user must accept the permissions that we require, so it's very crucial.
On my WatchKit extension, I wanted to display a simple warning for users who had not finished onboarding within our phone app yet.
As such, I thought I'd get the status of onboarding from the phone in two ways:
When the user opens the app/the app is activated (I use the willActivate method to detect this)
When the app finishes onboarding it sends a message to the watch of its completion (if the extension is reachable, of course)
Both of these combined would ensure that the status of onboarding is always kept in sync with the watch.
I wrote the first possibility in, utilizing reply handlers to exchange the information. It worked just fine, without any troubles. The warning telling the user to complete disappears, the extension does not crash, and all is well.
I then wrote in the second possibility, of the extension being reachable when the user finishes onboarding (with the phone then directly sending the companion the new status of onboarding). My extension crashes when it receives this message, and I am stuck with this odd error.
Program ended with exit code: 0
My extension does not even get a chance to handle the new onboarding status, the extension just quits and the above error is given to me.
I am not hitting any sort of memory limit. I have read the technical Q&A which describes what a memory usage limit error looks like, and I don't receive any sort of output like that whatsoever. As well, before the extension should receive the message, this is what my memory consumption looks like.
I have monitored the memory consumption of the extension right after finishing onboarding, and I see not a single spike indicating that I've gone over any kind of threshold.
I have tried going line by line over the code which manages the onboarding error, and I cannot find a single reason that it would crash with this error. Especially since the reply handler method of fetching the onboarding status works so reliably.
Here is the code of how I'm sending the message to the watch.
- (void)sendOnboardingStatusToWatch {
if(self.connected){
[self.session sendMessage:#{
LMAppleWatchCommunicationKey: LMAppleWatchCommunicationKeyOnboardingComplete,
LMAppleWatchCommunicationKeyOnboardingComplete: #(LMMusicPlayer.onboardingComplete)
}
replyHandler:nil
errorHandler:^(NSError * _Nonnull error) {
NSLog(#"Error sending onboarding status: %#", error);
}];
}
}
(All LMAppleWatchCommunicationKeys are simply #define'd keys with exactly their key as the string value. ie. #define LMAppleWatchCommunicationKey #"LMAppleWatchCommunicationKey")
Even though it's never called by the extension, here is the exact receiving code of the extension which handles the incoming data, if it helps.
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message {
NSString *key = [message objectForKey:LMAppleWatchCommunicationKey];
if([key isEqualToString:LMAppleWatchCommunicationKeyOnboardingComplete]){
BOOL newOnboardingStatus = [message objectForKey:LMAppleWatchCommunicationKeyOnboardingComplete];
[[NSUserDefaults standardUserDefaults] setBool:newOnboardingStatus
forKey:LMAppleWatchCommunicationKeyOnboardingComplete];
dispatch_async(dispatch_get_main_queue(), ^{
for(id<LMWCompanionBridgeDelegate> delegate in self.delegates){
if([delegate respondsToSelector:#selector(onboardingCompleteStatusChanged:)]){
[delegate onboardingCompleteStatusChanged:newOnboardingStatus];
}
}
});
}
}
Before including this onboarding-related code, my WatchKit extension was tested by over 100 people, without any troubles. I am using the exact same custom error dialogue that I was using before, just with a different string. I cannot for the life of me figure out what is causing this crash, and the ambiguity of it has given me very little to work with.
Any help would be greatly appreciated. Thank you very much for taking your time to read my post.
Edit: I just tried creating a symbolic breakpoint for exit(), which is never hit. If I call exit() myself, it calls the breakpoint, so I know the breakpoint itself is working.

isRegisteredForRemoteNotifications locking UI with semaphore_wait_trap

There's a strange situation happening in my iOS application when it receive push notification. The UI stay locked and nothing works. When I pause the debugger I see semaphore_wait_trap in my thread.
Debbuging the code I can see it is related to two things:
the value type in push notification (because when I change Number to String the problem disappear);
the isRegisteredForRemoteNotifications method (because when I remove it the problem disappear);
I'm receiving a push notification as follow
{aps:
{alert: { loc-args: [Fiat, Bravo, 501],
loc-key: SOME_TEXT
},
badge: 0,
sound: default.aiff
}
}
I made a new and simple project in Xcode to prove what I'm saying. I'm using the previous bundle identifier to receive the same push.
Follow the code in AppDelegate that shows the problem:
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"My token is: %#", deviceToken);
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
// [DefaultMethods saveInUserDefaults:#(1) forKey:kUserWasAskedForNotificationKey];
NSLog(#"Failed to get token, error: %#", error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if( [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] ){
NSLog(#"Success");
}
}
#end
Thank you for any help!
I was dealing with this problem too and have found this error in my device's logs:
com.apple.usernotifications.usernotificationservice: Exception caught
during decoding of received message, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
Exception: value for key 'NS.objects' was of unexpected class
'NSNumber'. Allowed classes are '{(
NSString,
NSArray )}'.
After calling isRegisteredForRemoteNotifications the application has stopped.
We have fixed this issue on our server and problem went off. Good luck.
I was having the same stall issue.
It turns out that I was also getting a push notification parsing error on the console (like the one mentioned above by #CFIFOK).
"NSXPCConnection: ---" connection to service named com.apple.usernotifications.usernotificationservice:
Exception caught during decoding of received message, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
Exception: value for key 'NS.objects' was of unexpected class 'NSNumber'.
Allowed classes are '{(
NSString,
NSArray
)}'.
This was due to the "title-loc-args" : [3333] not accepting 3333 literally but accepting it as a string "title-loc-args" : ["3333"]. This little thing made my entire interface stall after I accessed the mentioned method isRegisteredForRemoteNotifications.
One thing to take into account is that this stall only happens on iOS 11. It works perfectly fine on iOS 12.0 (16A5366a).
In order to debug the error I used Pusher app (https://github.com/noodlewerk/NWPusher) and traced down the argument that was giving me the parsing error.
Hope this helps!

An Objective-C message was sent to a deallocated '__NSDictionaryI' object (zombie) at address 0x1

I have an app using Multipeer Connectivity to send a string from one iOS device to another iOS device. It works and doesn't work.
The app is pretty much the app seen on the tutorial of this link: http://www.appcoda.com/intro-multipeer-connectivity-framework-ios-programming/
Sometimes, the app will send about 30 messages and then crashes, other times, it will crash on the 1st or 2nd message.
Xcode is giving me an EXC_BAD_ACCESS for the crash.
I used NSZombie to give some assistance, and it has provided me with the below, but I am fairly new and unable to understand the reason behind the crashes.
I'f the crash is due to the NSDictionary's, there are only 2 in my application:
-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state{
NSDictionary *dict = #{#"peerID": peerID,
#"state" : [NSNumber numberWithInt:state]
};
[[NSNotificationCenter defaultCenter] postNotificationName:#"MCDidChangeStateNotification"
object:nil
userInfo:dict];
}
-(void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{
NSDictionary *dict = #{#"data": data,
#"peerID": peerID
};
[[NSNotificationCenter defaultCenter] postNotificationName:#"MCDidReceiveDataNotification"
object:nil
userInfo:dict];
}
Any suggestions?
Zombies generally come when you try to access any deallocated object.
If you are using ARC, then you don't need to worry much about Zombies, X code does it for you but there are some cases in which zombies come in ARC project also. I never had this kind of issue.
If you are using Non ARC project, then you need to deallocate the objects manually.
As your error message suggests, you have used a dictionary's object that is not allocated or deallocated already and you are trying to use it again.
If you want to see where the zombie is, then please click on the arrow that is shown after the message in alert. You will be redirected to the line which caused your Zombie.
Please update your code according to that.
Hope this helps you.

Sending silent push notifications using Parse.com

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.

Can Firebase send and receive in the background on iOS 7?

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.

Resources