iOS/Sources/Shared/Environment/AppConstants.swift

199 lines
7.4 KiB
Swift

import Foundation
import KeychainAccess
import UIKit
import Version
/// Contains shared constants
public enum AppConstants {
/// Home Assistant Blue
public static var tintColor: UIColor {
#if os(iOS)
return UIColor { [lighterTintColor, darkerTintColor] (traitCollection: UITraitCollection) -> UIColor in
traitCollection.userInterfaceStyle == .dark ? lighterTintColor : darkerTintColor
}
#else
return lighterTintColor
#endif
}
public static var lighterTintColor: UIColor {
UIColor(hue: 199.0 / 360.0, saturation: 0.99, brightness: 0.96, alpha: 1.0)
}
public static var darkerTintColor: UIColor {
UIColor(hue: 199.0 / 360.0, saturation: 0.99, brightness: 0.67, alpha: 1.0)
}
/// Help icon UIBarButtonItem
#if os(iOS)
public static var helpBarButtonItem: UIBarButtonItem {
with(UIBarButtonItem(
icon: .helpCircleOutlineIcon,
target: nil,
action: nil
)) {
$0.accessibilityLabel = L10n.helpLabel
}
}
#endif
/// The Bundle ID used for the AppGroupID
public static var BundleID: String {
let baseBundleID = Bundle.main.bundleIdentifier!
var removeBundleSuffix = baseBundleID.replacingOccurrences(of: ".APNSAttachmentService", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".Intents", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".NotificationContentExtension", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".TodayWidget", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".watchkitapp.watchkitextension", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".watchkitapp", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".Widgets", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".ShareExtension", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".PushProvider", with: "")
removeBundleSuffix = removeBundleSuffix.replacingOccurrences(of: ".Matter", with: "")
return removeBundleSuffix
}
public static var deeplinkURL: URL {
switch Current.appConfiguration {
case .debug:
return URL(string: "homeassistant-dev://")!
case .beta:
return URL(string: "homeassistant-beta://")!
default:
return URL(string: "homeassistant://")!
}
}
/// The App Group ID used by the app and extensions for sharing data.
public static var AppGroupID: String {
"group." + BundleID.lowercased()
}
public static var AppGroupContainer: URL {
let fileManager = FileManager.default
let groupDir = fileManager.containerURL(forSecurityApplicationGroupIdentifier: AppConstants.AppGroupID)
guard let groupDir else {
fatalError("Unable to get groupDir.")
}
return groupDir
}
public static var appGRDBFile: URL {
let fileManager = FileManager.default
let directoryURL = Self.AppGroupContainer.appendingPathComponent("databases", isDirectory: true)
if !fileManager.fileExists(atPath: directoryURL.path) {
do {
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)
} catch {
Current.Log.error("Failed to create App GRDB file")
}
}
let databaseURL = directoryURL.appendingPathComponent("App.sqlite")
return databaseURL
}
public static var LogsDirectory: URL {
let fileManager = FileManager.default
let directoryURL = AppGroupContainer.appendingPathComponent("logs", isDirectory: true)
if !fileManager.fileExists(atPath: directoryURL.path) {
do {
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
} catch {
fatalError("Error while attempting to create data store URL: \(error)")
}
}
return directoryURL
}
public static var DownloadsDirectory: URL {
let fileManager = FileManager.default
let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .allDomainsMask).first!
.appendingPathComponent(
"Downloads",
isDirectory: true
)
if !fileManager.fileExists(atPath: directoryURL.path) {
do {
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
} catch {
fatalError("Error while attempting to create downloads path URL: \(error)")
}
}
return directoryURL
}
/// An initialized Keychain from KeychainAccess.
public static var Keychain: KeychainAccess.Keychain {
KeychainAccess.Keychain(service: BundleID)
}
/// A permanent ID stored in UserDefaults and Keychain.
public static var PermanentID: String {
let storageKey = "deviceUID"
let defaultsStore = UserDefaults(suiteName: AppConstants.AppGroupID)
let keychain = KeychainAccess.Keychain(service: storageKey)
if let keychainUID = keychain[storageKey] {
return keychainUID
}
if let userDefaultsUID = defaultsStore?.object(forKey: storageKey) as? String {
return userDefaultsUID
}
let newID = UUID().uuidString
if keychain[storageKey] == nil {
keychain[storageKey] = newID
}
if defaultsStore?.object(forKey: storageKey) == nil {
defaultsStore?.setValue(newID, forKey: storageKey)
}
return newID
}
public static var build: String {
SharedPlistFiles.Info.cfBundleVersion
}
public static var version: String {
SharedPlistFiles.Info.cfBundleShortVersionString
}
static var clientVersion: Version {
// swiftlint:disable:next force_try
var clientVersion = try! Version(version)
clientVersion.build = build
return clientVersion
}
}
public extension Version {
static let canSendDeviceID: Version = .init(minor: 104)
static let pedometerIconsAvailable: Version = .init(minor: 105)
static let tagWebhookAvailable: Version = .init(minor: 114, prerelease: "b5")
static let actionSyncing: Version = .init(minor: 115, prerelease: "any0")
static let localPushConfirm: Version = .init(major: 2021, minor: 10, prerelease: "any0")
static let externalBusCommandRestart: Version = .init(major: 2021, minor: 12, prerelease: "b6")
static let updateLocationGPSOptional: Version = .init(major: 2022, minor: 2, prerelease: "any0")
static let fullWebhookSecretKey: Version = .init(major: 2022, minor: 3)
static let conversationWebhook: Version = .init(major: 2023, minor: 2, prerelease: "any0")
static let externalBusCommandSidebar: Version = .init(major: 2023, minor: 4, prerelease: "b3")
static let externalBusCommandAutomationEditor: Version = .init(major: 2024, minor: 2, prerelease: "any0")
static let canUseAppThemeForStatusBar: Version = .init(major: 2024, minor: 7)
var coreRequiredString: String {
L10n.requiresVersion(String(format: "core-%d.%d", major, minor ?? -1))
}
}