Last active
October 26, 2021 18:34
-
-
Save tadija/c48177e26b76b541ee6ee620e15d407a to your computer and use it in GitHub Desktop.
AEVersion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://gist.github.com/tadija/c48177e26b76b541ee6ee620e15d407a | |
* Revision 4 | |
* Copyright © 2020-2021 Marko Tadić | |
* Licensed under the MIT license | |
*/ | |
import Foundation | |
public struct AEVersion: CustomStringConvertible { | |
public struct Version: Equatable, Comparable, Codable, CustomStringConvertible { | |
public let major: Int | |
public let minor: Int | |
public let patch: Int | |
public var description: String { | |
"\(major).\(minor).\(patch)" | |
} | |
public init(_ major: Int, _ minor: Int, _ patch: Int = 0) { | |
self.major = major | |
self.minor = minor | |
self.patch = patch | |
} | |
public init(_ value: String) { | |
let components = value.components(separatedBy: ".") | |
let major = components.indices ~= 0 ? Int(components[0]) ?? 0 : 0 | |
let minor = components.indices ~= 1 ? Int(components[1]) ?? 0 : 0 | |
let patch = components.indices ~= 2 ? Int(components[2]) ?? 0 : 0 | |
self = .init(major, minor, patch) | |
} | |
public static func < (lhs: Version, rhs: Version) -> Bool { | |
guard lhs != rhs else { return false } | |
let lhsComparators = [lhs.major, lhs.minor, lhs.patch] | |
let rhsComparators = [rhs.major, rhs.minor, rhs.patch] | |
return lhsComparators.lexicographicallyPrecedes(rhsComparators) | |
} | |
} | |
public enum State { | |
case new | |
case equal | |
case update(from: Version, to: Version) | |
case rollback(from: Version, to: Version) | |
init(old: Version?, new: Version) { | |
guard let old = old else { | |
self = .new; return | |
} | |
if old == new { | |
self = .equal | |
} else if old < new { | |
self = .update(from: old, to: new) | |
} else { | |
self = .rollback(from: old, to: new) | |
} | |
} | |
} | |
public let current: Version | |
public let history: [Version] | |
public var state: State { | |
State(old: history.last, new: current) | |
} | |
public var description: String { | |
"Version: \(current) | State: \(state) | History: \(history)" | |
} | |
public init(current: Version, history: [Version] = []) { | |
self.current = current | |
self.history = history | |
} | |
} | |
public extension AEVersion { | |
init(using defaults: UserDefaults = .standard) { | |
let current = Version(Self.getBundleVersion() ?? "") | |
var history = [Version]() | |
if let existingData = defaults.object(forKey: Key.history) as? Data, | |
let savedHistory = try? JSONDecoder() | |
.decode([Version].self, from: existingData) | |
{ | |
history.append(contentsOf: savedHistory) | |
} | |
let result = AEVersion(current: current, history: history) | |
switch result.state { | |
case .new, .update, .rollback: | |
history.append(current) | |
Self.replaceHistory(with: history, using: defaults) | |
case .equal: | |
break | |
} | |
self = result | |
} | |
static func getBundleVersion(from bundle: Bundle = .main) -> String? { | |
bundle.infoDictionary?["CFBundleShortVersionString"] as? String | |
} | |
static func replaceHistory(with history: [Version], | |
using defaults: UserDefaults = .standard) { | |
if let newData = try? JSONEncoder().encode(history) { | |
defaults.set(newData, forKey: Key.history) | |
defaults.synchronize() | |
} | |
} | |
private struct Key { | |
static let history = "AEVersion.History" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment