One of the first things every iOS developer should add to their project is a way to detect what is happening at launch. It doesn’t have to be complicated, but trust me, this can potentially save you some trouble later on.
I usually create a BaseAppDelegate class that will be responsible for detecting the following:
- is it the first time the user launch the app?
- how many times did the user launch count?
- is this launch the first launch after an app upgrade?
- when was last time the user launched the app before this launch?
Here is the code I usually put in this BaseAppDelegate class:
// | |
// BaseAppDelegate.swift | |
// | |
// Created by Cyril Chandelier on 21/04/2018. | |
// Copyright © 2018 Cyril Chandelier. All rights reserved. | |
// | |
import UIKit | |
class BaseAppDelegate: UIResponder, UIApplicationDelegate { | |
// MARK: – Dependencies | |
lazy var bundle: Bundle = Bundle.main | |
lazy var userDefaults: UserDefaults = UserDefaults.standard | |
// MARK: – UIApplicationDelegate methods | |
@discardableResult func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { | |
print("Initializing app") | |
// Try to detect first launch | |
if launchCount == 0 { | |
print("…first launch detected") | |
applicationFirstLaunch() | |
} | |
// Update app launches count | |
launchCount += 1 | |
print("…\(launchCount) launches") | |
// Try to detect version upgrade | |
let currentVersion = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") | |
as! String | |
if let lastLaunchVersion = lastLaunchVersion, currentVersion != lastLaunchVersion { | |
print("…upgrade detected") | |
applicationDidUpgrade(from: lastLaunchVersion, to: currentVersion) | |
} | |
// Update last known version if new | |
if lastLaunchVersion != currentVersion { | |
print("…saving new version: \(currentVersion)") | |
lastLaunchVersion = currentVersion | |
} | |
// Update last known launch date | |
print("…saving current launch date") | |
appLastLaunchDate = Date() | |
// Make sure user defaults are saved | |
userDefaults.synchronize() | |
return true | |
} | |
// MARK: – Optional methods to override | |
func applicationFirstLaunch() {} | |
func applicationDidUpgrade(from: String, to: String) {} | |
// MARK: – Utilities | |
private(set) var launchCount: Int { | |
get { | |
return userDefaults.integer(forKey: Keys.appLaunchCount) | |
} | |
set { | |
userDefaults.set(newValue, forKey: Keys.appLaunchCount) | |
} | |
} | |
private(set) var lastLaunchVersion: String? { | |
get { | |
return userDefaults.string(forKey: Keys.appLastLaunchVersion) | |
} | |
set { | |
if let newValue = newValue { | |
userDefaults.set(newValue, forKey: Keys.appLastLaunchVersion) | |
} else { | |
userDefaults.removeObject(forKey: Keys.appLastLaunchVersion) | |
} | |
} | |
} | |
private(set) var appLastLaunchDate: Date? { | |
get { | |
return userDefaults.object(forKey: Keys.appLastLaunchDate) as? Date | |
} | |
set { | |
if let newValue = newValue { | |
userDefaults.set(newValue, forKey: Keys.appLastLaunchDate) | |
} else { | |
userDefaults.removeObject(forKey: Keys.appLastLaunchDate) | |
} | |
} | |
} | |
// MARK: – User default keys | |
private struct Keys { | |
static let appLaunchCount = "appLaunchCount" | |
static let appLastLaunchVersion = "appLastLaunchVersion" | |
static let appLastLaunchDate = "appLastLaunchDate" | |
} | |
} |
You just need to make your AppDelegate extends BaseAppDelegate, and call the super method in application(_:didFinishLaunchingWithOptions). Then, you can choose (or not) to override (and therefore implement something for) the applicationFirstLaunch() and/or applicationDidUpgrade(from:to:) methods.
Here are a couple use cases where having this super-class reveals itself handy:
- I want to know how many times a users came into the app for my analytics
- I want to know when a users re-open the app after a while, to customize his experience as a returning user
- I want to show a “What’s new” popup to my returning users who upgraded their app
- my app was a paid app before, and now it’s free with In-App Purchases to unlock some content, but I don’t want my users who already paid to hit my paywall