There are a few ways your app can launch:
User opens app
Push notification leads to opening of app.
What is the recommended design to know when to make a async network call to your server to fetch some data? For example, didFinishLaunchingWithOptions gets called at app launcht ime, so would it make sense to put that call in that function? What about putting the call in viewDidLoad of my root view controller? viewDidLoad only gets called once for load while viewWillAppear gets called too frequently.
What's a good pattern or design to handle both cases of app launch.
Related
Just starting out iOS development.
When starting my app, I'd like it to check if the user has a known account and if they do, "login" by acquiring an access token and then display the main / first view. If they don't have an account or if login fails, they should be redirected to a login / registration screen.
Initially I thought I'd hide this process (check for account + call to get access token) behind a splash screen, but apparently this is against Apples guidelines. Is there a common / recommended way to do this on iOS?
By referencing to application:didFinishLaunchingWithOptions: you can read that this method is good for initializing.
Use this method (and the corresponding
application:willFinishLaunchingWithOptions: method) to complete your
app’s initialization and make any final tweaks. This method is called
after state restoration has occurred but before your app’s window and
other UI have been presented. At some point after this method returns,
the system calls another of your app delegate’s methods to move the
app to the active (foreground) state or the background state. This
method represents your last chance to process any keys in the
launchOptions dictionary. If you did not evaluate the keys in your
application:willFinishLaunchingWithOptions: method, you should look at
them in this method and provide an appropriate response. Objects that
are not the app delegate can access the same launchOptions dictionary
values by observing the notification named
UIApplicationDidFinishLaunchingNotification and accessing the
notification’s userInfo dictionary. That notification is sent
shortly after this method returns.
There may be several ways. But using singleton for token and launching your app is one of the best ways. As you might not need to use that launcher class again. Just for checking user have token and if yes then show main view otherwise login view.
Hope it helps you !!
I am developing an iPhone app with a Today Extension. The app has a Model module that loads from/saves toNSUserDefaults. Since I want this information to be available to both the main app and the extension, I use an app group:
let storage = NSUserDefaults(suiteName: "group.etc.etc.etc...")
Both the app and the extension can access the information without any problem.
The main app occasionally might create a local notification to present to the user. That notification has two actions associated with it (UIUserNotificationAction). One of those actions triggers some code run on the background on the main app. That code changes the NSUserDefaults information and triggers a synchronization. My code is something like this:
func application(application: UIApplication, handleActionWithIdentifier id: String?, forLocalNotification not: UILocalNotification, completionHandler: () -> ()) {
// Interact with model here
// New information gets saved to NSUserDefaults
userDefaultsStorage.synchronize()
completionHandler()
}
Now, on the Today Ext. I naturally observe any changes made to the information on NSUserDefaults so that I can reload the interface on the widget:
override func viewDidLoad() {
super.viewDidLoad()
// ...
NSNotificationCenter.defaultCenter().addObserverForName(NSUserDefaultsDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in
self.reload()
}
}
Now, here's my issue:
The main app schedules a UILocalNotification. I open the today view and look at my today widget.
When the notification fires, a banner appears on the top of the screen.
I slide down on that banner to reveal the two actions and I select the one that I mentioned earlier (the today widget is still live and on screen).
I know for a fact that the action runs correctly in the background, and that the changes are being made to the information on NSUserDefaults.
However, even though the today widget has been active and on screen all this time, no reload action is triggered. After further investigation, I can confirm that the NSUserDefaultsDidChangeNotification is not being fired (I placed a breakpoint and it did not trigger, and did some other checks as well).
I know the changes are being made by the notification action because if I force a reload of the widget (by closing and opening the today view) the widget updates correctly.
I have seen various tutorials online where the first thing they say is to listen to this notification and update the widget so that "the widget is in sync with NSUserDefaults". But the thing is that AFAICT this notification is absolutely useless! How come??
Note 1: When I change the information on NSUserDefaults from within the today widget the notification fires correctly.
Note 2: Debugging a today widget is absolutely horrible, btw. It is always necessary to tell Xcode to "Attach to process by name..." before it can react to breakpoints and crashes. And iOS is constantly creating a new process for the widget so I have to constantly tell Xcode to attach again.
From doc here:
Cocoa includes two types of notification centers:
The NSNotificationCenter class manages notifications within a single
process. The NSDistributedNotificationCenter class manages
notifications across multiple processes on a single computer.
Apparently the containing app and today extension are different processes, since when you debug today extension you want to attach containing app process, but NSNotificationCenter only work within a single process.
In order to communicate between containing app and extensions, you can use
Darwin Notify Center CFNotificationCenterthat works like NSDistributedNotificationCenter, which is only available for osx.
The idea is use a file inside the group folder that they share. in containing app, you write the data you want to send into the file, then post a CFNotification, which will be received by today extension.
In today extension, use CFNotificationCenterAddObserver to observer the CFNotification, upon receiving it, callback will be called, in which a NSNotification has to be posted due to callback is a C style function and "userInfo" cannot be passed in the CFNotification, after receiving this NSNotification object, it starts to read data from the file, which is used to update the today extension view in Notification center.
You can use this github code to implement force loading the today extension view. It works for me.
Here is a great post on this. http://www.atomicbird.com/blog/sharing-with-app-extensions
Another option is to use setHasContent function. When you schedule a local identifier, set has content to false to hide the view, in handleActionWithIdentifier set it to true to show the view. This way, when you stay in notification center, you will not see the view for a moment, but when you see it, it will be the updated data.
let widgetController = NCWidgetController.widgetController()
widgetController.setHasContent(false, forWidgetWithBundleIdentifier: "YourTodayWidgetBundleIdentifier")
But I think the whole problem is a rare case, which doesn't need to be fixed since you can get the updated data reloading the notification center or switch to notification tab and switch back to today tab.
I have an iOS application where I need to persist a set of data after the application HARD CLOSES (when the user double clicks the Home button and slides the application upwards). Then, when the application comes back to the foreground, I need to fetch that data and do something with it. I'm just not sure where I need to put that logic for Application Hard Close and Resuming the Application.
In your AppDelegate
When your app is going to be closed, but still in Multitasking menu the following method is getting called
-(void)applicationWillResignActive:(UIApplication*)application
If after 3 minutes user doesn re-open your app this method is going to be called
-(void)applicationDidEnterBackground:(UIApplication*)application
If user re-opens your app from multitasking menu the following method is getting called
-(void)applicationWillEnterForeground:(UIApplication*)application
If user is going to close your app from multitasking menu this method is getting called(you will have limited time to perform some logic here)
-(void)applicationWillTerminate:(UIApplication*)application
When user presses home twice applicationWillResignActive and applicationDidEnterBackground is called. You can save data here.
When user opens app, applicationWillEnterForeground is called, you get data which you save and process.
When a user hard-closes the application, the UIViewController's Delegate's method called applicationWillTerminate. Here I can catch and save the model data before it's all destroyed.
Then when a user launches the application again, there are many choices like didFinishLaunchingWithOptions where I can grab the data stored to disk.
Your app no longer gets an applicationWillTerminate call ever. You are simply silently killed while in the background. You have to write your app to save state in the applicationDidEnterBackground handler, as nmh describes. That's your only option.
My opens and edit a custom file type. I allow the user to open a downloaded file in my application, typically through email. However the file isn't passed to the application if the program is alreay running.
So if you don't have your app running you can use
didFinishLaunchingWithOptions
How ever if your app is already running it doesn't actually hit this method when the file is passed to your application. What is the method that does this for app going from the background to foreground?
If I understand the question correctly (and without looking at the title, which is not very descriptive) you should have a look at the UIApplicationDelegate methods applicationDidBecomeActive: and applicationWillEnterForeground: .
These methods don't get the document passed to them. Instead, after applicationWillEnterForeground: is called, the system will call application:openURL:sourceApplication:annotation: . In your implementation of this method you can retrieve the document.
On first view that is displayed to the user, I have to populate some text fields with some saved information on the phone.
Does apple have a diagram for the entire state of app-launchign sequence? Ie, that includes the app delegate method calls and the view delegate method calls? It seems that order of events switch for different iOS versions...
For example:
http://oleb.net/blog/2011/06/app-launch-sequence-ios/
Lets say you had a custom view controller in MainWindow.xib, won't viewWillAppear be called before didFinishLaunchingWithOptions?
To be safe, should I just wrap a quick call around all my sqlite3 database functions that check if the database is open before continuing, and open it if necessary?
I used an SQL Database in an iPhone application that put opened the database if it was closed inside a void called from viewDidLoad. It wasn't detrimental at all to the performance of the application.
Though in a newer app I needed to grab some preferences (not in an SQL Database this time) as the application opened so I created an instance of my ViewController and called it's reload preferences method from the App Delegate's -(void)applicationDidBecomeActive:(UIApplication *)application; method (Reruns the method every time my app becomes active) like so:
- (void)applicationDidBecomeActive:(UIApplication *)application{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
ViewController *instanceOfVC = [[[ViewController alloc] init]autorelease];
[instanceOfVC checkPreferences];
}