diff --git a/Sources/Packages/Sources/Brief/Documentation.docc/Documentation.md b/Sources/Packages/Sources/Brief/Documentation.docc/Documentation.md
new file mode 100644
index 0000000..fe1562c
--- /dev/null
+++ b/Sources/Packages/Sources/Brief/Documentation.docc/Documentation.md
@@ -0,0 +1,15 @@
+# ``Brief``
+
+Brief is a collection of protocols and concrete implmentation describing updates.
+
+## Topics
+
+### Versioning
+
+- ``SemVer``
+- ``Release``
+
+### Updater
+
+- ``UpdaterProtocol``
+- ``Updater``
diff --git a/Sources/Packages/Sources/Brief/Release.swift b/Sources/Packages/Sources/Brief/Release.swift
new file mode 100644
index 0000000..847dffe
--- /dev/null
+++ b/Sources/Packages/Sources/Brief/Release.swift
@@ -0,0 +1,80 @@
+import Foundation
+
+/// A release is a representation of a downloadable update.
+public struct Release: Codable {
+
+ /// The user-facing name of the release. Typically "Secretive 1.2.3"
+ public let name: String
+
+ /// A boolean describing whether or not the release is a prerelase build.
+ public let prerelease: Bool
+
+ /// A URL pointing to the HTML page for the release.
+ public let html_url: URL
+
+ /// A user-facing description of the contents of the update.
+ public let body: String
+
+ /// Initializes a Release.
+ /// - Parameters:
+ /// - name: The user-facing name of the release.
+ /// - prerelease: A boolean describing whether or not the release is a prerelase build.
+ /// - html_url: A URL pointing to the HTML page for the release.
+ /// - body: A user-facing description of the contents of the update.
+ public init(name: String, prerelease: Bool, html_url: URL, body: String) {
+ self.name = name
+ self.prerelease = prerelease
+ self.html_url = html_url
+ self.body = body
+ }
+
+}
+
+extension Release: Identifiable {
+
+ public var id: String {
+ html_url.absoluteString
+ }
+
+}
+
+extension Release: Comparable {
+
+ public static func < (lhs: Release, rhs: Release) -> Bool {
+ lhs.version < rhs.version
+ }
+
+}
+
+extension Release {
+
+ /// A boolean describing whether or not the release contains critical security content.
+ /// - Note: this is determined by the presence of the phrase "Critical Security Update" in the ``body``.
+ /// - Warning: If this property is true, the user will not be able to dismiss UI or reminders associated with the update.
+ public var critical: Bool {
+ body.contains(Constants.securityContent)
+ }
+
+ /// A ``SemVer`` representation of the version number of the release.
+ public var version: SemVer {
+ SemVer(name)
+ }
+
+ /// The minimum macOS version required to run the update.
+ public var minimumOSVersion: SemVer {
+ guard let range = body.range(of: "Minimum macOS Version"),
+ let numberStart = body.rangeOfCharacter(from: CharacterSet.decimalDigits, options: [], range: range.upperBound..
Bool {
+ for (latest, current) in zip(lhs.versionNumbers, rhs.versionNumbers) {
+ if latest < current {
+ return true
+ } else if latest > current {
+ return false
+ }
+ }
+ return false
+ }
+
+
+}
diff --git a/Sources/Packages/Sources/Brief/Updater.swift b/Sources/Packages/Sources/Brief/Updater.swift
index ded6b7a..c71e538 100644
--- a/Sources/Packages/Sources/Brief/Updater.swift
+++ b/Sources/Packages/Sources/Brief/Updater.swift
@@ -1,22 +1,24 @@
import Foundation
import Combine
-public protocol UpdaterProtocol: ObservableObject {
-
- var update: Release? { get }
- var testBuild: Bool { get }
-}
-
+/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
public class Updater: ObservableObject, UpdaterProtocol {
@Published public var update: Release?
-
public let testBuild: Bool
+ /// The current OS version.
private let osVersion: SemVer
+ /// The current version of the app that is running.
private let currentVersion: SemVer
- public init(checkOnLaunch: Bool, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) {
+ /// Initializes an Updater.
+ /// - Parameters:
+ /// - checkOnLaunch: A boolean describing whether the Updater should check for available updates on launch.
+ /// - checkFrequency: The interval at which the Updater should check for updates. Subject to a tolerance of 1 hour.
+ /// - osVersion: The current OS version.
+ /// - currentVersion: The current version of the app that is running.
+ public init(checkOnLaunch: Bool, checkFrequency: TimeInterval = Measurement(value: 24, unit: UnitDuration.hours).converted(to: .seconds).value, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) {
self.osVersion = osVersion
self.currentVersion = currentVersion
testBuild = currentVersion == SemVer("0.0.0")
@@ -24,12 +26,13 @@ public class Updater: ObservableObject, UpdaterProtocol {
// Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet.
checkForUpdates()
}
- let timer = Timer.scheduledTimer(withTimeInterval: 60*60*24, repeats: true) { _ in
+ let timer = Timer.scheduledTimer(withTimeInterval: checkFrequency, repeats: true) { _ in
self.checkForUpdates()
}
timer.tolerance = 60*60
}
+ /// Manually trigger an update check.
public func checkForUpdates() {
URLSession.shared.dataTask(with: Constants.updateURL) { data, _, _ in
guard let data = data else { return }
@@ -38,6 +41,8 @@ public class Updater: ObservableObject, UpdaterProtocol {
}.resume()
}
+ /// Ignores a specified release. `update` will be nil if the user has ignored the latest available release.
+ /// - Parameter release: The release to ignore.
public func ignore(release: Release) {
guard !release.critical else { return }
defaults.set(true, forKey: release.name)
@@ -50,6 +55,8 @@ public class Updater: ObservableObject, UpdaterProtocol {
extension Updater {
+ /// Evaluates the available downloadable releases, and selects the newest non-prerelease release that the user is able to run.
+ /// - Parameter releases: An array of ``Release`` objects.
func evaluate(releases: [Release]) {
guard let release = releases
.sorted()
@@ -66,49 +73,18 @@ extension Updater {
}
}
+ /// Checks whether the user has ignored a release.
+ /// - Parameter release: The release to check.
+ /// - Returns: A boolean describing whether the user has ignored the release. Will always be false if the release is critical.
func userIgnored(release: Release) -> Bool {
guard !release.critical else { return false }
return defaults.bool(forKey: release.name)
}
+ /// The user defaults used to store user ignore state.
var defaults: UserDefaults {
UserDefaults(suiteName: "com.maxgoedjen.Secretive.updater.ignorelist")!
}
-}
-
-public struct SemVer {
-
- let versionNumbers: [Int]
-
- public init(_ version: String) {
- // Betas have the format 1.2.3_beta1
- let strippedBeta = version.split(separator: "_").first!
- var split = strippedBeta.split(separator: ".").compactMap { Int($0) }
- while split.count < 3 {
- split.append(0)
- }
- versionNumbers = split
- }
-
- public init(_ version: OperatingSystemVersion) {
- versionNumbers = [version.majorVersion, version.minorVersion, version.patchVersion]
- }
-
-}
-
-extension SemVer: Comparable {
-
- public static func < (lhs: SemVer, rhs: SemVer) -> Bool {
- for (latest, current) in zip(lhs.versionNumbers, rhs.versionNumbers) {
- if latest < current {
- return true
- } else if latest > current {
- return false
- }
- }
- return false
- }
-
}
@@ -119,63 +95,3 @@ extension Updater {
}
}
-
-public struct Release: Codable {
-
- public let name: String
- public let prerelease: Bool
- public let html_url: URL
- public let body: String
-
- public init(name: String, prerelease: Bool, html_url: URL, body: String) {
- self.name = name
- self.prerelease = prerelease
- self.html_url = html_url
- self.body = body
- }
-
-}
-
-extension Release: Identifiable {
-
- public var id: String {
- html_url.absoluteString
- }
-
-}
-
-extension Release: Comparable {
-
- public static func < (lhs: Release, rhs: Release) -> Bool {
- lhs.version < rhs.version
- }
-
-}
-
-extension Release {
-
- public var critical: Bool {
- body.contains(Constants.securityContent)
- }
-
- public var version: SemVer {
- SemVer(name)
- }
-
- public var minimumOSVersion: SemVer {
- guard let range = body.range(of: "Minimum macOS Version"),
- let numberStart = body.rangeOfCharacter(from: CharacterSet.decimalDigits, options: [], range: range.upperBound..Summary
+
+## Overview
+
+Text
+
+## Topics
+
+### Group
+
+- ``Symbol``
diff --git a/Sources/Packages/Sources/SecretKit/Documentation.docc/Documentation.md b/Sources/Packages/Sources/SecretKit/Documentation.docc/Documentation.md
index 6888611..8555c39 100644
--- a/Sources/Packages/Sources/SecretKit/Documentation.docc/Documentation.md
+++ b/Sources/Packages/Sources/SecretKit/Documentation.docc/Documentation.md
@@ -1,4 +1,4 @@
-# ````SecretKit````
+# ``SecretKit``
SecretKit is a collection of protocols describing secrets and stores.
diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/Documentation.docc/Documentation.md b/Sources/Packages/Sources/SecureEnclaveSecretKit/Documentation.docc/Documentation.md
index 1a1f46b..d153175 100644
--- a/Sources/Packages/Sources/SecureEnclaveSecretKit/Documentation.docc/Documentation.md
+++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/Documentation.docc/Documentation.md
@@ -1,3 +1,3 @@
-# ````SecureEnclaveSecretKit````
+# ``SecureEnclaveSecretKit``
SecureEnclaveSecretKit contains implementations of SecretKit protocols backed by the Secure Enclave.
diff --git a/Sources/Packages/Sources/SmartCardSecretKit/Documentation.docc/Documentation.md b/Sources/Packages/Sources/SmartCardSecretKit/Documentation.docc/Documentation.md
index b17f751..0826de9 100644
--- a/Sources/Packages/Sources/SmartCardSecretKit/Documentation.docc/Documentation.md
+++ b/Sources/Packages/Sources/SmartCardSecretKit/Documentation.docc/Documentation.md
@@ -1,3 +1,3 @@
-# ````SmartCardSecretKit````
+# ``SmartCardSecretKit``
SmartCardSecretKit contains implementations of SecretKit protocols backed by a Smart Card.