Files
SideStore/AltStore/Unstable Features/UnstableFeatures.swift
2023-05-20 10:48:52 -07:00

102 lines
3.9 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// UnstableFeatures.swift
// SideStore
//
// Created by naturecodevoid on 5/20/23.
// Copyright © 2023 SideStore. All rights reserved.
//
// I prefixed it with Available to make UnstableFeatures come up first in autocomplete, feel free to rename it if you know a better name
enum AvailableUnstableFeature: String, CaseIterable {
// The value will be the GitHub Issue number. For example, "123" would correspond to https://github.com/SideStore/SideStore/issues/123
//
// Unstable features must have a GitHub Issue for tracking progress, PRs and feedback/commenting.
case jitUrlScheme = "0"
/// Dummy variant to ensure there is always at least one variant. DO NOT USE!
case dummy = "dummy"
func availableOutsideDevMode() -> Bool {
switch self {
// If your unstable feature is stable enough to be used by nightly users who are not alpha testers or developers,
// you may want to have it available in the "Unstable Features" menu in Settings (outside of dev mode). To do so, add this:
//case .yourFeature: return true
case .jitUrlScheme: return true
default: return false
}
}
}
class UnstableFeatures {
#if UNSTABLE
private static var features: [AvailableUnstableFeature: Bool] = [:]
#endif
static func load() {
#if UNSTABLE
if features.count > 0 { return print("It seems unstable features have already been loaded, skipping") }
if let rawFeatures = UserDefaults.shared.unstableFeatures,
let rawFeatures = try? JSONDecoder().decode([String: Bool].self, from: rawFeatures) {
for rawFeature in rawFeatures {
if let feature = AvailableUnstableFeature.allCases.first(where: { feature in String(describing: feature) == rawFeature.key }) {
features[feature] = rawFeature.value
} else {
print("Unknown unstable feature: \(rawFeature.key) = \(rawFeature.value)")
}
}
save(load: true)
} else {
print("Setting all unstable features to false since we couldn't load them from UserDefaults (either they were never saved or there was an error decoding JSON)")
for feature in AvailableUnstableFeature.allCases {
features[feature] = false
}
save()
}
#else
print("Unstable features are not available on this build")
#endif
}
private static func save(load: Bool = false) {
#if UNSTABLE
var rawFeatures: [String: Bool] = [:]
for feature in features {
rawFeatures[String(describing: feature.key)] = feature.value
}
UserDefaults.shared.unstableFeatures = try! JSONEncoder().encode(rawFeatures)
print("\(load ? "Loaded" : "Saved") unstable features: \(String(describing: rawFeatures))")
#else
// we want this to crash, this function should never be triggered on non-unstable builds
fatalError("Tried to save unstable features on non-unstable build!")
#endif
}
static func set(_ feature: AvailableUnstableFeature, enabled: Bool) {
#if UNSTABLE
features[feature] = enabled
save()
#else
// we want this to crash, this function should never be triggered on non-unstable builds
fatalError("Tried to set unstable feature \(String(describing: feature)) to \(enabled) on non-unstable build!")
#endif
}
@inline(__always) // hopefully this will help the compiler realize that if statements that use this function should be removed on non-unstable builds
static func enabled(_ feature: AvailableUnstableFeature) -> Bool {
#if UNSTABLE
features[feature] ?? false
#else
false
#endif
}
}