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.
Related
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];
}
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.
In my app I have Singleton object, which should save its state through app launches. So I need to save it somehow.
I see only two options: 1) save it on app termination (plus, maybe, going background); 2) save it each time any property changed.
First option looks bad, because app can be killed, for example, because of some bug, memory limits or device power-off (low battery). So I expect state won't be saved.
Second option needs either manual notifications about each change, or KVO + observing of each property.
Seems that I do something wrong here. Maybe, you can give me some advice or there is some well-known pattern (I've tried to google, but found nothing particular).
UPDATE:
Yes, there is NSUserDefaults, but to improve its usability (smth. more than just key-values) I would write wrapper-methords, so I will end with the same problem (lines of manual coding).
UPDATE2:
CoreData is also bad choice for me: just one object to store + inserting there also needs some more lines of code.
UPDATE3:
It's not a question about "how to save". It's about "how to call this saving automatically (or with less of coding)". So in NSUserDefault way we need to manually implement each property as wrapper. In NSCoding - call save or post notification (to catch & save from one place) from each property also.
The simplest way to save user states in iOS is through NSUserDefaults.
Here is an example which keeps track of all changed made to your singleton:
#interface MySingleton : NSObject
{
}
+ (MySingleton *)sharedSingleton;
#property (copy) NSString *userName;//the variable to track
#end
#implementation MySingleton
#synthesize userName;
+ (MySingleton *)sharedSingleton
{
static MySingleton *sharedSingleton;
#synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
- (void)setUserName:(NSString*)userName
{
//update the variable
self.userName = userName;
//saves the new value to NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:userName forKey:#"userName"];
//forces instantaneous synchronization, not needed, would be updated after a periodic interval anyways
[[NSUserDefaults standardUserDefaults] synchronize];
}
#end
Optionally you can implement the NSCoding protocol and save the whole class to NSUserDefaults, take a look at this question.
I think the first option is the better way to do it. The OS always informs the app when it will be killed for example for memory warnings the didReceiveMemoryWarning method will be called where you can save the states to the singleton object. (Device power off is same as go to background so the applicationWillEnterForeground method will be called).
I want to store some of my application data in iCloud, and I want it to be saved when the application is going to the background or is shut down.
I have a MyDocument class, which is written in accordance with this Apple tutorial. In fact, I do this:
// this function called from applicationDidEnterBackground
void SaveICloudData( const char *rawData )
{
MyDocument *doc = AppDelegate.getICloudDocument; // MyDocument is subclass of UIDocument, like in tutorial
NSString *str = [[NSString alloc] initWithUTF8String:rawData];
[doc setDocumentText:str];
}
My app goes to the background and is synchronized only when I turn it on again. But I want to synchronize my data with iCloud manually. How can I do this?
Before exiting, UIDocument checks if the document hasUnsavedChanges. If YES, saving is invoked.
You are setting the document text in the applicationDidEnterBackground delegate method, which is probably too late.
From the method docs:
You should perform any tasks relating to adjusting your user interface
before this method exits but other tasks (such as saving state) should
be moved to a concurrent dispatch queue or secondary thread as needed.
Because it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
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