ios logins using NSUserDefaults, handle crash or device shutdown - ios

I’m trying to handle critical behavior when user login on my ipad application.
Once successfully logged in, we will remember the login using nsuserdefaults but will require a re-login when:
Crash occurred
Device was shutdown
is it possible to reset the login value on NSUserDefault when the above actions occurred ? if Yes how can I handle them ?
thanks

For case 1 add an exception handler
- (void) applicationDidFinishLaunching: (UIApplication *) application
{
NSSetUncaughtExceptionHandler (&myExceptionHandler);
}
....
- (void) myExceptionHandler (NSException *exception)
{
// Make a note in your default settings
// User needs to log in
}
For case 2 trap
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Make a note in your default settings
// User needs to log in
}

I don't know of a way for an app to know when the device is powered off. If you meant when the application is exited read on.
Or just stick the logic in the
-(void)applicationDidBecomeActive:(UIApplication *)application
And catch both scenarios.

Related

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!

When you close the application, boolean not set to no

I use Firebase in the application. I want to record the user's status in the database, it is currently in the application or not. I use the code from the official document, which must ensure this task. When the application is opened, the value is set to NO, and then changed to YES. But at the close of the application as well as the transition to the background, the block is not called and the value does not change. Here is my code...
FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:#".info/connected"];
[connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
if([snapshot.value boolValue]) {
NSLog(#"CONNECTED");
} else {
NSLog(#"NOT CONNECTED");
}
}];
2017-01-17 13:36:38.203 Tricker[6293:817528] NOT CONNECTED
2017-01-17 13:36:40.863 Tricker[6293:817528] CONNECTED
The console comes the following information, which shows that the value changes after two seconds after the opening of the application with NO to YES. But nothing happens when the application is closed...
Prompt in what could be the problem?
You seem to be confusing connection state and application lifecycle.
When it is active, the Firebase Database client will fire information about its connection state on the .info/connected path.
But when the app is not active, the client has no way of firing this information anymore. You will need to use regular iOS application lifecycle events to detect when the app becomes inactive.
Recording the user's presence in the database works as a two-step process:
when the application starts you write a value to the database to signal that the user is online
when the application starts, you tell the database server to write another value to the database that signals that the user is gone.
Step 2 is accomplished by onDisconnectSetValue:
[presenceRef onDisconnectSetValue:#"I disconnected!"];
The trick is that you call this method early on, typically when your app starts. The write operation will be executed when the Firebase Database server detects that the client disconnected.
This disconnect can happen in two ways:
when the client closes the connection explicitly
when the the socket that the server uses to communicate with the client times out
When your app crashes, you're in situation 2. In that case it can take a few minutes before the server detects that the client is gone, since you're waiting for a socket to time-out.
You close the app means ? two things possibility
User can put app in Background :
Here you can set a NSUSEDefault value like :
(void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setBool:NO
forKey:#"YOUR_KEY"]; [[NSUserDefaults standardUserDefaults]
synchronize];
}
User can remove app from background :
(void)applicationWillTerminate:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:#"YOUR_KEY"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

iOS saving data if app crash

I have got requirement in one of my projects where user is asking to save data locally in case of sudden app crashes. Its form based app so user fill lots of data in form and if app crashes suddenly then user lost all data entered.
App is using Core Data to save data locally.
There is a point where we save entered data in core data but not during every second user fills data.
Also if app crashes core data also vanishes.
In fact, if your app crash, the callback :
- (void)applicationWillTerminate:(UIApplication *)application
will not be called. This one is only called if for some reasons the system need to shutdown your app (usually because resources are rare or because your background job is still doing work after the maximum time allowed) or if you are on < iOs 4. You don't have any way to know when your app will crash (but when you relaunch the app, you can know if it had crashed).
So for your particular case you have two solutions :
Use either a NSTimer with a quick firing rate, or call a fonction each time you edit a field and update the core-data context then save it on disk.
NSError *error = nil;
[managedObjectContext save:&error]
Did you set a persistentStoreCoordinator on your context ? If no, core-data will never persist your data on disk.
Crashes don't appear out of nowhere, find the place(s) where crashes might happen and fix it or if you can't, use a try-catch to keep your app running (but please, try not to do that...).
Hope this help
You can implement a HandleException to catch all exceptions that crash your application.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//for uncaughted exceptions
NSSetUncaughtExceptionHandler(&HandleException);
struct sigaction signalAction;
memset(&signalAction, 0, sizeof(signalAction));
signalAction.sa_handler = &HandleSignal;
sigaction(SIGABRT, &signalAction, NULL);
sigaction(SIGILL, &signalAction, NULL);
sigaction(SIGBUS, &signalAction, NULL);
//and more if you need to handle more signals
//another setup ...
return YES
}
#pragma mark crash management
void HandleException(NSException *exception) {
NSLog(#"[FALTAL] App crashing with exception: %#", exception);
//try to save your DB here.
}
void HandleSignal(int signal) {
//try to save your DB here.
}
#pragma -
However, I don't know about how many time you will have before application exits. But I suppose that you will have enough time to do the DB-backup task.
In particular case you can should use, try catch block, (but not) everywhere.
try {
//write your code
}catch(NSException *e){
//See what's the error
}finally{
//Save your context
}
This it the best solution in my thinking. However you can create a NSTimer which executes a method at some reasonable seconds where you can hold and save your context.
You can also save your context in AppDelegate's method like (if you're targeting iOS 4.0 and above and if your app was exit by iOS it self for some reason),
- (void)applicationWillTerminate:(UIApplication *)application{};
below method will always call when your app goes in background,
- (void)applicationDidEnterBackground:(UIApplication *)application{};
Use
(void)applicationWillTerminate:(UIApplication *)application
delegate method of AppDelgate to save your data in core data.

Firebase PresenceManaging iOS

I'm implementing a system based on firebase docs:
[connectionMonitor observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
if([snapshot.value boolValue]) {
// connection established (or I've reconnected after a loss of connection)
// add this device to my connections list
// this value could contain info about the device or a timestamp instead of just true
Firebase * con = [[Firebase alloc]initWithUrl:[NSString stringWithFormat:#"%#Users/%#/connections/", urlString, currentUserId]];
Firebase * newConnection = [con childByAutoId];
[newConnection setValue:#YES];
// when this device disconnects, remove it
[newConnection onDisconnectRemoveValue];
}
}];
Which works fine, if the user fully disconnects, but that's my problem.
I use this system to see if the user is online. If they're not online, I trigger a push notification. If the user closes the app, firebase doesn't disconnect, but it also doesn't receive updates, so on the other end, the user looks like they are still online. For the firebase onDisconnect value to properly set, the user is required to completely close out of the app.
I have resolved this by adding:
- (void)applicationWillResignActive:(UIApplication *)application
{
[Firebase goOffline];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[Firebase goOffline];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[Firebase goOnline];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[Firebase goOnline];
}
Is this normal behavior, or am I doing something wrong?
This is (currently) expected behavior. Firebase won't trigger the presence actions until the client actually disconnects and iOS will leave the underlying socket connection alive for some period of time (probably less than 5 minutes) after the app goes to the background... so presence will be delayed. It should still definitely happen eventually though.
Your workaround should work fine, or to avoid tearing down the whole connection, you could just set the presence bit to #NO / #YES on going to background / foreground.
I can see how most apps would expect presence to kick in when the app goes to the background, so we may investigate changing this behavior in the future.

Do I initialize the viewController when I receive UIApplicationLaunchOptionsLocationKey in app didFinishLaunchingWithOptions?

I'm creating an app which listens to significant location change events and in case the app gets terminated then the iOS launches the app with UIApplicationLaunchOptionsLocationKey set.
So, the documentation says to create a new location manager and register for location updates again. However, doesn't mention if I'm supposed to initialize my viewController (as I do in normal app launch as well)? My view controllers initialize in viewDidLoad but are created in appDidFinishLaunchingWithOptions.
Any idea how much time does OS provides to the App for location update handling? My app needs to make a webservice request if the location change indicates an interested location for the app.
Thanks
You should consider moving your initialization code to a new method, something like initializeViews. This method would check to make sure the views haven't been initialized and then initialize them. You would call this method from application:didFinishLaunchingWithOptions: and applicationWillEnterForeground:, but the call in application:didFinishLaunchingWithOptions: would only occur if the application wasn't going to the background.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
if([UIApplication sharedApplication].applicationState != UIApplicationStateBackground)
[self initializeViews];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[self initializeViews];
}
- (void)initializeViews {
if(!viewsAreInitialized) {
...
viewsAreInitialized = YES;
}
}

Resources