In this article, I will show you how to implement ** a mechanism that tells the user that a new version has been released when the app is launched and prompts for an update (called a semi-forced update in this article) **.
Forced update is a mechanism that you often see in game apps. By making the app unavailable until you update it, you can always run with the latest version. However, it is inconvenient not to be able to use it when you want to use it immediately because you are not always in a communication environment where users can update. From the point of view of such usability, I like the semi-forcedness that I kept before prompting the update.
By the way, if there is a discrepancy in the version of the application installed by the user, it will be difficult to modify the existing function as well as not being able to provide the latest function. For example, when migrating a DB for several generations, I sometimes step on an unexpected bug that the version is flying.
It is desirable to have users always use the latest version, and I think it is better to actively adopt a mechanism to encourage updates.
When implementing the semi-forced update mechanism, the following flow can be considered.
iTunes Search API
You may prepare your own server to return the latest version, but here is the [iTunes Search API](https://affiliate.itunes.apple.com/resources/documentation/itunes-store-web-service-search- Use api /). Using this API, you can get information on apps distributed on the App Store for free. The app information also includes the version information released on the App Store.
By using the iTunes Search API, ** the version information of the app that can be obtained will be automatically updated when the app is actually delivered from the App Store **, so the delivered version by yourself It is easy to operate because there is no need to set up a server to manage iTunes and you will not forget to update the latest version.
I have implemented an AppStore class that hits the iTunes Search API like this: (I am using Alamofire v5 for the communication library)
import Foundation
import Alamofire
typealias LookUpResult = [String: Any]
enum AppStoreError: Error {
case networkError
case invalidResponseData
}
class AppStore {
private static let lastCheckVersionDateKey = "\(Bundle.main.bundleIdentifier!).lastCheckVersionDateKey"
static func checkVersion(completion: @escaping (_ isOlder: Bool) -> Void) {
let lastDate = UserDefaults.standard.integer(forKey: lastCheckVersionDateKey)
let now = currentDate
//Skip until the date changes
guard lastDate < now else { return }
UserDefaults.standard.set(now, forKey: lastCheckVersionDateKey)
lookUp { (result: Result<LookUpResult, AppStoreError>) in
do {
let lookUpResult = try result.get()
if let storeVersion = lookUpResult["version"] as? String {
let storeVerInt = versionToInt(storeVersion)
let currentVerInt = versionToInt(Bundle.version)
completion(storeVerInt > currentVerInt)
}
}
catch {
completion(false)
}
}
}
static func versionToInt(_ ver: String) -> Int {
let arr = ver.split(separator: ".").map { Int($0) ?? 0 }
switch arr.count {
case 3:
return arr[0] * 1000 * 1000 + arr[1] * 1000 + arr[2]
case 2:
return arr[0] * 1000 * 1000 + arr[1] * 1000
case 1:
return arr[0] * 1000 * 1000
default:
assertionFailure("Illegal version string.")
return 0
}
}
///Open App Store
static func open() {
if let url = URL(string: storeURLString), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
}
private extension AppStore {
static var iTunesID: String {
"<YOUR_ITUNES_ID>"
}
///App Store App Page
static var storeURLString: String {
"https://apps.apple.com/jp/app/XXXXXXX/id" + iTunesID
}
/// iTunes Search API
static var lookUpURLString: String {
"https://itunes.apple.com/lookup?id=" + iTunesID
}
///Returns an integer such as 20201116 generated from the current date and time
static var currentDate: Int {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.locale = .current
formatter.dateFormat = "yyyyMMdd"
return Int(formatter.string(from: Date()))!
}
static func lookUp(completion: @escaping (Result<LookUpResult, AppStoreError>) -> Void) {
AF.request(lookUpURLString).responseJSON(queue: .main, options: .allowFragments) { (response: AFDataResponse<Any>) in
let result: Result<LookUpResult, AppStoreError>
if let error = response.error {
result = .failure(.networkError)
}
else {
if let value = response.value as? [String: Any],
let results = value["results"] as? [LookUpResult],
let obj = results.first {
result = .success(obj)
}
else {
result = .failure(.invalidResponseData)
}
}
completion(result)
}
}
}
extension Bundle {
/// Info.Get the version number in the plist. major.minor.It is assumed that it is in patch format
static var version: String {
return Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
}
}
Replace <YOUR_ITUNES_ID>
with the iTunes ID of the app for which you want to get version information. The iTunes ID is displayed in the URL when you open the app page on the App Store in your browser. Similarly, replace storeURLString
with the URL of the App Store.
In the above implementation, the version is checked for updates once a day. For version comparison, the version string in * major.minor.patch * format is divided by dots and converted to Int type (see the versionToInt (_ :)
method). With the above method, minor and patch can only take 1000 steps from 0 to 999, but that's enough.
When using the AppStore class, create the following method in ViewController and call viewDidAppear
or UIApplication.willEnterForegroundNotification
with the observed method.
private extension ViewController {
func checkVersion() {
AppStore.checkVersion { (isOlder: Bool) in
guard isOlder else { return }
let alertController = UIAlertController(title: "There is a new version!", message: "Please update.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "update", style: .default) { action in
AppStore.open()
})
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel))
self.present(alertController, animated: true)
}
}
}
With the above implementation, it is now possible to inform the user that a new version has been released.