Skip to content

Instantly share code, notes, and snippets.

@tadija
Last active October 26, 2021 18:34
Show Gist options
  • Save tadija/c48177e26b76b541ee6ee620e15d407a to your computer and use it in GitHub Desktop.
Save tadija/c48177e26b76b541ee6ee620e15d407a to your computer and use it in GitHub Desktop.
AEVersion
/**
* 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