When you close the application, boolean not set to no - ios

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];
}

Related

Read local storage when iOS app enters background

Objective-c is extremely new to me:
The method below triggers when my iphone app moves to the background. In that moment I want to grab the items in UIWebView's local storage and store in the app.
I'm getting the error "No known class for selector 'stringByEvaluatingJavaScriptFromString:jsString'". How do I inject this dependency into this function while still keeping UIApplication?
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
//Persist local storage data
NSLog(#"Persisting local storage since app entering background");
//Reading local storage item
NSString *jsString = #"localStorage.getItem('mpo.subdomain');";
NSString *someKeyValue = [UIWebView stringByEvaluatingJavaScriptFromString:jsString];
// Store your subdomain in iPhone persistence variable and use it later in the application
NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];
[userdefault setObject:someKeyValue forKey:#"subdomain"];
[userdefault synchronize];
//use of User default
NSLog(#"User Subdomain %#",[userdefault valueForKey:#"subdomain"]);
}
The code [UIWebView stringByEvaluatingJavaScriptFromString:] is attempting to invoke a CLASS method called stringByEvaluatingJavaScriptFromString on the UIWebview class. The stringByEvaluatingJavaScriptFromString method is an instance method. You need to send that message to an instance of UIWebView, not the class.
If you have a property myWebView then you'd use the code:
[self.myWebView stringByEvaluatingJavaScriptFromString:]
That sends the stringByEvaluatingJavaScriptFromString message to the instance of UIWebview pointed to by the self.myWebView property. That's what you want.

watchOS 2.0 - Can't cancel WCSessionFileTransfer

Our app lets a user select records on iPhone that they want to be displayed in the watch app.
It works like this:
The user taps "Add to watch" on a record from their iPhone
A new version of the watch database is generated and sent to the watch
The watch app receives and saves the file and updates its interface
A new database file is sent to the watch and processed for each change. This is fine if the watch is awake since it will give the user live updates, but if the watch is asleep while the user makes 7 changes, it means the watch is accepting and processing 7 new files as soon as it wakes up.
We really only care about the most recent version of the watch database, so I'm trying to cancel all old outstanding file transfers.
Code:
On iPhone, each time a record is added/removed from watch database, we attempt (unsuccessfully) to cancel pending file transfers and then queue the latest database file:
// create watch database and store it at self.urlToDatabase
[self generateNewWatchDatabase];
if ([WCSession isSupported])
{
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
// this is the problem - cancel doesn't seem to do anything
for (WCSessionFileTransfer *fileTransfer in session.outstandingFileTransfers)
[fileTransfer cancel];
[session transferFile:self.urlToDatabase metadata:nil];
}
In the above code, calling [fileTransfer cancel] successfully removes the WCSessionFileTransfer object from session.outstandingFileTransfers, but didReceiveFile is still being called multiple times below.
Accepting the file on the watch:
- (void)session:(WCSession *)session didReceiveFile:(WCSessionFile *)file
{
// this method gets called once for every time -transferFile:metadata: is called above,
// even after cancelling outstanding file transfers
[self replaceDatabaseWithFile:file];
[self refreshItemsTable];
}
How do we cancel outstanding file transfers?
Edit
As per #ccjensen's recommendation, I tried the following in the method that fires when the user adds/removes a record to/from the watch:
// store a reference to the file transfer and immediately cancel it
WCSessionFileTransfer *transfer = [session transferFile:self.urlToDatabase metadata:nil];
[transfer cancel];
This still results in the file being sent to the watch, instead of cancelling it as one would expect.
I also tried the following:
Kill watch app (by holding the side button until 'Power Off' appears, and then holding it again)
Add/remove records from iPhone
Relaunch watch app
Even in this scenario the watch receives all 'cancelled' file transfers.
The documentation for the cancel method says:
Use this method to cancel a file transfer before it completes. If the file has already been transferred, calling this method has no effect.
So it sounds like the cancels are "best effort" and might not end up being able to cancel them in all cases, especially if the file has already been transferred.
Are you seeing it never work, even if you call cancel immediately (try testing without the watch app running as that seems to expedite the transfers)?
turned out the reason this worked was that the file url did not match the transfer's url I was checking 🙈
I recently found that keeping hold of the WCSessionFileTransfer in my own array and canceling them proved more reliable than using [WCSession defaultSession].outstandingFileTransfers.
NSMutableArray<WCSessionFileTransfer*>* inProgressTransfers = [NSMutableArray array];
So each time you call TransfeFile: metaData:
WCSessionFileTransfer* transfer = [[WCSession defaultSession] transferFile:url metadata:metadata];
if (transfer)
{
[self.inProgressTransfers addObject:transfer];
}
then at an appropriate time
for (WCSessionFileTransfer* ourTransfer in self.inProgressTransfers)
{
[ourTransfer cancel];
[self.inProgressTransfers removeObject:ourTransfer];
}
For some reason, keeping hold of the transfers ourself makes calling cancel work much more reliably.. hope that helps someone

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.

Player in game stopped playing

I'm working on a game in iOS, which needs an internet connection. The problem I'm having is setting a Bool on the server (parse.com) when the player stops playing. Right now I'm doing this...
-(void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Player logged out");
[self playerLoggedOut];
}
and
-(void)playerLoggedOut
{
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
[currentUser setObject:[NSNumber numberWithBool:NO] forKey:#"playing"];
[currentUser saveEventually];
[sharedInstance requestSentWithDesc:#"Player logged out"];
}
}
But that doesn't seem to be working, there's also the situation of the game crashing, when I believe the above would never get called.
There's also...
-(void)applicationWillTerminate:(UIApplication *)application
But I'm not sure when's that called.
There is the possibility of using a timer, but if the player never logs back in, or doesn't for a long time I'm not sure how a timer could be used.
For your current processing you would need to start a background task (see here) which starts before you make the request and ends in the completion block (so change the save method you use).
For the other cases this won't work and you may want to add some kind of 'keep-alive' message an / or parse cloud code which monitors the user interaction and automatically logs them out if they are inactive for a set time.
If your game is crashing, you must get any logs, for example:
Or something else. You can add NSLog(#"worked to the bitter end!"); at the end of your -(void)playerLoggedOut or requestSentWithDesc method.

iPhone setting NSUserDefaults to nil when app is closed or in background

I want to eliminate all of the variables saved into all fields of NSUserDefaults whenever the app is closed or running in the background for a certain amount of time - say 5 minutes.
I tried to add a line to the app delegate of applicationDidFinishLaunching that looks like this:
if (UIApplicationStateBackground == TRUE) {
NSUserDefaults *profiles = [NSUserDefaults standardUserDefaults];
[profiles setObject:nil forKey:#"name1"];
[profiles synchronize];
}
I also added just this portion to the applicationWillTerminate:
NSUserDefaults *profiles = [NSUserDefaults standardUserDefaults];
[profiles setObject:nil forKey:#"name1"];
[profiles synchronize];
None of this seems to be working and I have no idea how to set a condition of 'if 5 minutes have surpassed of the application being in the background, delete the NSUserDefaults variables' - Any help?
I would recommend to remove the object instead of setting it to nil.
- (void)removeObjectForKey:(NSString *)defaultName;
The normal behavior of NSUserDefaults is to return nil when there is no key matching the query, so I believe is better to follow the same rule and not store nil for a certain key.
Hope it helps.
Maybe you should use applicationDidEnterBackground:. Check out this apple doc page.
I would do the following: in the delegate call - (void)applicationWillResignActive:(UIApplication *)application of UIApplication I would save a timestamp when the application entered the background. In this case you do not have to check if the application is going to the background or if it just got interrupted by e.g. an SMS but the user hit cancel and continues using your application.
When the application launches again implement code in another delegate method of UIApplication - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions to determine how long you have been inactive and wipe the UserDefaults or not depending on the duration.
For more information of application becoming inactive and relaunching check this document of Apple's documentation http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/CoreApplication/CoreApplication.html#//apple_ref/doc/uid/TP40007072-CH3-SW10

Resources