I am registering for Push Notification by calling the following piece of code:
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound];
But in some rare cases following delegate does not get called:
- (void)application:(UIApplication *)iApplication didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)iNewDeviceToken {
Not even following method gets called:
- (void)application:(UIApplication *)iApplication didFailToRegisterForRemoteNotificationsWithError:(NSError *)iError {
What could be the reason for this. I am proceeding with my app flow once I received the device token. So, in some rare scenarios my app hungs.
Any clue?
According to the documentation, neither of the callbacks will happen until the device has a persistent connection with the push server. So if there is no wifi or data connection available, the callbacks won't happen - and apple doesn't regard this as an error condition. As far as I can tell, the only errors that can happen to cause the didFail... callback are an incorrect certificate/app permissions issue (a development problem), or the user declined permission (though I am only sporadically able to reproduce this by changing the date and turning the phone off).
The clue is right there: you get a NSError instance when it fails. So to figure out why it errors, inspect the NSError and see what its telling you.
You should probably at least handle that case in your app as well, such as showing an error message along the lines of "Failed to register application, please try again...".
One more thing: If you phone is rooted/jail broken for use with another network, etc., you will have problems. My phone was rooted and none of the delegates were called. I had a coworker put it on their iPad, and it worked fine, so I know the code was OK.
So, I reinstalled the OS on my phone, and it started working.
You have to enable the Push Notifications in the provisioning profile, set it up in the Developer Portal with the App Id. Then it should work.
Related
(It looks like this issue has been encountered by others in previous weeks, but there haven't been any solutions that I've found.)
I'm trying to do a really basic thing: Get data from either my iOS app or my Watch app to my Complication Controller.
I am turning out to be way less capable of getting this done than I thought. watchOS 2 Transition Guide indicates that I should "[fetch] the needed data from the extension delegate" using the following code:
ExtensionDelegate* myDelegate = [[WKExtension sharedExtension] delegate];
NSDictionary* data = [myDelegate.myComplicationData objectForKey:ComplicationCurrentEntry];
Great. Except, I haven't been able to figure out how to get this to work on the extension side. Though even more importantly, I can't seem to even get the extension delegate code to run at all from a complication controller launch. When I run the complication, I get this message: "Extension received request to wake up for complication support." However, none of the code within any of the extension delegate's methods seems to run. I've also set breakpoints within every method and none of those breakpoints are hit.
It also looks like "transferCurrentComplicationUserInfo:" is also suggested to be used for complication updates, though it's unclear precisely how it's used. As much as I've gathered, it's used to wake up the watch extension so that ExtensionDelegate can store the new data for the next time the complication controller runs, but due to the previous issue I haven't been able to confirm.
I've got one maybe workaround (pinging the server from the complication controller and hoping that session variables persist so I can send relevant data), but there's every chance that if I can't get this worked out my complication work will be hosed. Any help here would be tremendous.
By the way, here's the code I have for "getCurrentTimelineEntryForComplication", if that's helpful at all.
- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler {
NSDate* entryDate = [NSDate date];
ExtensionDelegate* myDelegate = [[WKExtension sharedExtension] delegate];
NSString* data = [myDelegate.complicationData objectForKey:#"meow"];
NSLog(#"complication data: %#", data);
CLKComplicationTimelineEntry* entry = [self getTimelineEntry:#"2015-08-25 00:19:42" entryDate:entryDate complication:complication];
handler(entry);
}
I've been working with Complications in WatchOS2 since Xcode 7 Beta 4. I'm now on the latest, Xcode Beta 6. I've had a number of issues as in both Beta versions running on the Watch, running on the iPhone then installing to the Watch, and running on the simulator frequently give false negatives due to what appears to be buggy APIs and OS releases. I have been able to get data to show on complications in the following way.
Ensure that your primary Interface Controller implements the WCSessionDelegate protocol.
Implement both the didReceiveMessage and didReceiveApplicationContext methods in your Interface Controller.
In your iPhone app, attempt to send a message using the WCSession to the Watch.
If the message fails to send from the iPhone app, send the application context.
Back in the Interface Controller, when you receive a message -or- a context, update the values in your Extension Delegate.
Still in the Interface Controller and still after receiving a message -or- context, get a handle to the CLKComplicationServer and for each complication in activeComplications call reloadTimelineForComplication.
In your Complication Controller's getCurrentTimelineEntryForComplication grab the data you set in the Extension Delegate and set the values in your CLKComplicationTimelineEntry.
This will work usually when the App is already open on the Watch, the app is still resident in memory, but backgrounded on the Watch, or you start the app and their is context waiting which it consumes.
I have not been able to get the historical timeline entries to function (or the future ones). Nor, I have I been able to get the timeline to update independently of the Watch app.
If you are having trouble, here are some debugging things to try. As I stated above, the API and OS appears to be very buggy. The steps below do work (sometimes).
In the sim, use the Reset all Settings option on both the iPhone and Watch sim.
On the device, restart the Watch. If necessary, unpair and repair the Watch, although this takes a really long time to do.
On the iPhone, delete the app (which will also delete the Watch app if installed) and reinstall.
I hope that helps!
Justin
In order to make the ComplicationController respond to WCSession activity you must make the controller conform to WCSessionDelegate, then manage didReceiveUserInfo from within the ComplicationController. The ExtensionDelegate is not woken up for these updates when in the background. You can still update your delegate from the controller if necessary.
Also, as of right now, the simulator does not send transferCurrentComplicationUserInfo to the watch sim, you have to test on devices.
I'm seeing something very strange, and I'm hoping someone here can help me. Anytime I close/background my iOS app, the last push notification sent to that app via UrbanAirship pops-up at the top of the screen.
I've setup push notifications for my iOS app using UrbanAirship, and its associated libraries. I'm using the latest library from UA (1.3.3), and I believe I've configured everything correctly. I've gone over the basic documentation multiple times, and I'm not doing anything out of the ordinary that I can tell.
In my AppDelegate, I'm not doing anything with the UA libraries in the functions applicationWillResignActive or applicationDidEnterBackground, so I'm not certain why backgrounding the app would trigger this notification to come up. The documentation doesn't state that anything needs to happen in those functions. In the function applicationWillTerminate, I am correctly calling [UAirship land], but this issue isn't popping up when the app is terminated.
More data: I'm not actually seeing a new notification in the Notification center on my phone. I'm just seeing the message pop-up again, as if it were new.
This issue is perplexing, and if anyone can offer some insight or assistance with it, I'd appreciate it.
I'm running into a bit of an issue.
My software needs to know when the application is in background so it will disconnect from the server, and start receiving push notifications.
For that I use UIApplicationDelegate's method :
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidBecomeActive:(UIApplication *)application
- (void)applicationWillTerminate:(UIApplication *)application
The problem is, when a jailbroken device is using the "tweak" called backgrounder, that will force your application to stay active and not going into background, none of the UIApplicationDelegate's method get called when we click on the HOME button.
The thing is, each client connected in SSL cost me a lot of memory on the server. I do not really care that the device is jailbroken, or as a matter a fact, the application will run indefinitely on the device. But I would like them to disconnect from the server when the application is no longer on the screen, but I can't seems to find any method that will inform me of such operation when backgrounder is installed.
Solution find : check my own answer on the post
The best thing to do would to eat up A LOT of memory so that way the jail breakers would have to quit your application! Just kidding of course. The best option is to wait for inactivity on the user's side. For example: 1. Check if the device is jailbroken. 2. Check if backgrounder is installed (I am not sure if you can do this but I am pretty sure you can). 3. If the user is all those things wait for them to be inactive for, lets say, 30 seconds and then disconnect from the server.
Do none of those methods get called at all in your app when Backgrounder is installed, or only when your app transitions to the background? If they never get called at all, then the solution is extremely simple: Set a timer for, say, 10 seconds, when your app's application:didFinishLaunchingWithOptions method is called, and when that timer expires, if applicationDidBecomeActive: has not been called, then you know that the user is using Backgrounder.
If they do get called on app launch, though, then things get trickier. I'm not aware of any direct methods to detect if a tweak like Backgrounder is installed from within the sandbox. You could simply check if the device is jailbroken (there are a number of ways to do this, they should be easy to find), and add a timeout period for your server connection if so.
Milk Tea got me on the right track... So the bounty goes to him.
But actually I got a better solution...
I have checked every single notifications thrown when resigning the App...
and I got this, even with backgrounder configured to keep the App open, those notifications are thrown :
UIApplicationSuspendedEventsOnlyNotification -- When going into background
UIApplicationResumedEventsOnlyNotification -- When going into foreground
This makes sense, since even with backgrounder, it would have been a bad idea to let the App keep receiving events notifications, for example, when a change of rotation (or a shake) is done outside the App. Do you imagine every App doing a rotation on background everytime you move your iphone ?
I have a question about CLLocationManagerDelegate. The documentation says if the user changes the settings for your location services (in the iPhone's Settings.app) then your app is supposed to receive an didChangeAuthorizationStatus: message to the delegate. My question is, when would this happen?
If the user changed the setting, it means they are in the settings app, and your app is either backgrounded or not running at all, so in the former case, when would your app's CLLocationManager delegate get the didChangeAuthorizationStatus: call?
I just ran across this method an hour ago, so good timing on the question!
It looks like in my case this method gets called:
When the app becomes active.
On allowing Location Services for the app on the initial startup of the app.
I wrote a quick test app you can find here:
https://github.com/mharper/LocationServices
It simply logs the authorization status whenever the method gets called.
This delegate method will be called when:
The first time you init a CLLocationManager instance
1.1. If that's the first time your App launch on device, you'll receive state kCLAuthorizationStatusNotDetermined before user see the [Allow/Don't Allow] UIAlertView. (At this time, you can find that the UISwitch of your App in Settings - Privacy - Location Service is turn off or not shown.
1.2. When user re-launch your App. Because decision has been made in previous launch, so you can retrieve the state.
The first time after user made decision. This is obvious. After you call the startUpdatingLocation/startUpdatingHeading, then iOS show the UIAlertView.
In your answer's situation, user made changes in Settings, if your App is running in background, you'll receive the new state when your App become active. Otherwise, reference 1.2.
If your app is running in the background or not at all it will be called the moment the user returns to your application.
I encounter the same issue as well.
my solution is put the request gps authorization code in main loop.
I guess it can also works if you put in another run loop.
I ran into this problem of handling location permission changes correctly recently and did a lot of research and debugging.
If the app is not running at all and the user changes location permissions in Settings, then when you start your app, locationManager:didChangeAuthorizationStatus is called when the location manager is initialized as stated in mharper's answer. This behavior is not in Apple's docs.
If the app is in the background/suspended, I tested it in the simulator and it looks like the delegate function is also called.
my problem is this:
I have scheduled some UILocalNotifications on an iOS app, the problem is that if I delete the app without deleting the objects associated with the notifications (and consequently removing the notifications from the scheduledNotifications array) the notifications still fire.
Although I have not experienced them actually firing (they are set to repeat within a week's interval) I have evidence of this since I NSLog the scheduledNotifications array at application launch, which even after deleting the app and reinstalling it (with the entities of the data model gone), still shows some scheduled notifications.
I've searched the UIApplication and the UIApplicationDelegate reference in Apple's reference library but I found no method to know if the app is being deleted, if there is I would just write
[[UIApplication sharedApplication] cancelAllLocalNotifications];
in said method.
So, is there a way for knowing this?
Thank you in advance, your help is very much appreciated.
This was bug in 4.0 that the local notification was repeated even if the user has deleted the app, but at least that bug was fixed by Apple in later release.