diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index ff1799ba0c..1256340def 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -195,7 +195,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { log.error("Failed to copy default keyboard from bundle: \(error)") } } - Migrations.engineVersion = Version.current + Migrations.engineVersion = Version.latestFeature if Util.isSystemKeyboard || Storage.active.userDefaults.bool(forKey: Key.keyboardPickerDisplayed) { isKeymanHelpOn = false diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Migrations.swift b/ios/engine/KMEI/KeymanEngine/Classes/Migrations.swift index 6ae03a82e1..b37e55c2f7 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Migrations.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Migrations.swift @@ -159,7 +159,7 @@ public enum Migrations { static func updateResources(storage: Storage) { var lastVersion = engineVersion - if (lastVersion ?? Version.fallback) >= Version.current { + if (lastVersion ?? Version.fallback) >= Version.latestFeature { // We're either current or have just been downgraded; no need to do modify resources. // If it's a downgrade, it's near-certainly a testing environment. return @@ -267,7 +267,7 @@ public enum Migrations { } // Store the version we just upgraded to. - storage.userDefaults.lastEngineVersion = Version.current + storage.userDefaults.lastEngineVersion = Version.latestFeature } static func migrateUserDefaultsToStructs(storage: Storage) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Model/Version.swift b/ios/engine/KMEI/KeymanEngine/Classes/Model/Version.swift index b457dbd1bb..ac29ed86aa 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Model/Version.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Model/Version.swift @@ -11,7 +11,16 @@ import Foundation /// Dotted-decimal version. public struct Version: Comparable { public static let fallback = Version("1.0")! - public static let current = Version("13.0.65")! + public static let latestFeature = Version("13.0.65")! + + public static var current: Version { + // TODO: actually update KMEI's version in the bundle during builds, not just the app's. + //let engineInfo = Bundle(for: Manager.self).infoDictionary + + // For now, we just rely on the app's version instead. + let engineInfo = Bundle.main.infoDictionary + return Version(engineInfo!["CFBundleVersion"] as! String)! + } // The Engine first started tracking the 'last loaded version' in 12.0. public static let freshInstall = Version("0.0")! @@ -27,7 +36,9 @@ public struct Version: Comparable { public let string: String public init?(_ string: String) { - let stringComponents = string.components(separatedBy: ".") + let tagComponents = string.components(separatedBy: "-") + let stringComponents = tagComponents[0].components(separatedBy: ".") + var components: [Int] = [] for s in stringComponents { guard let i = Int(s), i >= 0 else { @@ -40,6 +51,26 @@ public struct Version: Comparable { self.components = components } + public init?(_ components: [Int]) { + if components.count == 0 { + return nil + } + + var string = "" + + for i in components { + guard i >= 0 else { + return nil + } + + let dot = (string == "" ? "" : ".") + string = "\(string)\(dot)\(i)" + } + + self.string = string + self.components = components // Swift arrays are value types, not reference types! + } + public static func <(lhs: Version, rhs: Version) -> Bool { let len = max(lhs.components.count, rhs.components.count) for i in 0..= 2) { + return Version([self.components[0], self.components[1]])! + } else { + // If we somehow have just a major version, append a simple '0'. + return Version([self.components[0], 0])! + } + } } diff --git a/ios/engine/KMEI/KeymanEngineTests/VersionTests.swift b/ios/engine/KMEI/KeymanEngineTests/VersionTests.swift index e2a0bc8e32..2c14fe8849 100644 --- a/ios/engine/KMEI/KeymanEngineTests/VersionTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/VersionTests.swift @@ -11,34 +11,72 @@ import XCTest class VersionTests: XCTestCase { - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } - func testVersionComparison() { - // Simple unit testing for version-comparison logic. - let simpleEarly = Version("11.0")! - let simpleLate = Version("13.0")! + func testStringConstruction() { + let simpleEarly = Version("11.0") - XCTAssertLessThan(simpleEarly, simpleLate) + XCTAssertNotNil(simpleEarly, "Could not construct Version instance from a simple major-minor version string") - let complexEarly1 = Version("11.0.65")! - let complexEarly2 = Version("11.1.1")! + let complexEarly = Version("11.0.65.17.8") - XCTAssertLessThan(complexEarly1, complexEarly2) + XCTAssertNotNil(complexEarly, "Could not process the version string '11.0.65.17.8'") - let complexLate1 = Version("13.0.1")! + let broken = Version("apple.orange") - XCTAssertLessThan(complexEarly2, complexLate1) - XCTAssertLessThan(simpleLate, complexLate1) + XCTAssertNil(broken, "Erroneously constructed Version instance from character-text strings") - let simpleLater = Version("13.1")! + let tagged = Version("14.0.18-alpha-local") - XCTAssertLessThan(complexLate1, simpleLater) - } + XCTAssertNotNil(tagged, "Could not construct Verison instance from a text-tagged version string") + XCTAssertEqual(tagged, Version("14.0.18"), "Did not produce expected version from text-tagged version string") + } + func testVersionComparison() { + // Simple unit testing for version-comparison logic. + let simpleEarly = Version("11.0")! + let simpleLate = Version("13.0")! + + XCTAssertLessThan(simpleEarly, simpleLate) + + let complexEarly1 = Version("11.0.65")! + let complexEarly2 = Version("11.1.1")! + + XCTAssertLessThan(complexEarly1, complexEarly2) + + let complexLate1 = Version("13.0.1")! + + XCTAssertLessThan(complexEarly2, complexLate1) + XCTAssertLessThan(simpleLate, complexLate1) + + let simpleLater = Version("13.1")! + + XCTAssertLessThan(complexLate1, simpleLater) + } + + func testMajorMinor() { + let simple = Version("12")! + + XCTAssertEqual(simple.majorMinor, Version("12.0")!, "Did not append .minor to major-only version") + + let complex = Version("11.0.65.17.8")! + + XCTAssertEqual(complex.majorMinor, Version("11.0")!, "Did not properly trim off excess version components") + } + + func testValidCurrentEngineVersion() { + let version = Version.current + + // Do not test on value; this may mismatch with the actual version, since the test host doesn't get + // version number updates from builds. + // + // The key is that it always returns a version value; this is the most likely 'big break' we may see. + XCTAssertNotNil(version, "Could not construct a valid version from value in bundle's plist file") + } } diff --git a/ios/keyman/Keyman/Keyman/InfoViewController/InfoViewController.swift b/ios/keyman/Keyman/Keyman/InfoViewController/InfoViewController.swift index 00b7437ea8..86b30467d9 100644 --- a/ios/keyman/Keyman/Keyman/InfoViewController/InfoViewController.swift +++ b/ios/keyman/Keyman/Keyman/InfoViewController/InfoViewController.swift @@ -61,7 +61,7 @@ class InfoViewController: UIViewController, UIWebViewDelegate { } private func loadFromServer() { - let appVersion = Version.current + let appVersion = Version.current.majorMinor let url = "https://help.keyman.com/products/iphone-and-ipad/\(appVersion.string)/?embed=ios" webView.loadRequest(URLRequest(url: URL(string: url)!)) log.debug("Info page URL: \(url)") diff --git a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.swift b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.swift index 806d450ac0..0d6c2a82f9 100644 --- a/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.swift +++ b/ios/keyman/Keyman/Keyman/SetUpViewController/SetUpViewController.swift @@ -68,7 +68,7 @@ class SetUpViewController: UIViewController, UIWebViewDelegate { } private func loadFromServer() { - let appVersion = Version.current + let appVersion = Version.current.majorMinor let url = "https://help.keyman.com/products/iphone-and-ipad/\(appVersion.string)" + "/installing-system-keyboard.php?embed=ios" webView.loadRequest(URLRequest(url: URL(string: url)!))