用于 FirebaseRemoteConfig 的现代 Swift API
SwiftyRemoteConfig 通过将富有表现力的 Swifty API 与静态类型的优势相结合,使 Firebase Remote Config 的使用变得愉快。这个库深受 SwiftyUserDefaults 的启发。
由于 Xcode 编译器错误,您需要使用权变方法才能在 Xcode 13.3 或更高版本中使用此库。以下是推荐的权变方法步骤。
SwiftyRemoteConfig 的模块中创建 SwiftyRemoteConfig+Workaround.swift 文件。SwiftyRemoteConfig+Workaround.swift 中。 这几乎是 Sources 文件夹中 BuiltIns.swift 文件的副本:https://raw.githubusercontent.com/fumito-ito/SwiftyRemoteConfig/master/Sources/SwiftyRemoteConfig/BuiltIns.swiftimport Foundation
import SwiftyRemoteConfig
extension RemoteConfigSerializable {
public static var _remoteConfigArray: RemoteConfigArrayBridge<[T]> { RemoteConfigArrayBridge() }
}
extension Date: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigObjectBridge<Date> { RemoteConfigObjectBridge() }
}
extension String: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigStringBridge { RemoteConfigStringBridge() }
}
extension Int: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigIntBridge { RemoteConfigIntBridge() }
}
extension Double: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigDoubleBridge { return RemoteConfigDoubleBridge() }
}
extension Bool: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigBoolBridge { RemoteConfigBoolBridge() }
}
extension Data: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigDataBridge { RemoteConfigDataBridge() }
}
extension URL: RemoteConfigSerializable {
public static var _remoteConfig: RemoteConfigUrlBridge { RemoteConfigUrlBridge() }
public static var _remoteConfigArray: RemoteConfigCodableBridge<[URL]> { RemoteConfigCodableBridge() }
}
extension RemoteConfigSerializable where Self: Codable {
public static var _remoteConfig: RemoteConfigCodableBridge<Self> { RemoteConfigCodableBridge() }
public static var _remoteConfigArray: RemoteConfigCodableBridge<[Self]> { RemoteConfigCodableBridge() }
}
extension RemoteConfigSerializable where Self: RawRepresentable {
public static var _remoteConfig: RemoteConfigRawRepresentableBridge<Self> { RemoteConfigRawRepresentableBridge() }
public static var _remoteConfigArray: RemoteConfigRawRepresentableArrayBridge<[Self]> { RemoteConfigRawRepresentableArrayBridge() }
}
extension RemoteConfigSerializable where Self: NSCoding {
public static var _remoteConfig: RemoteConfigKeyedArchiverBridge<Self> { RemoteConfigKeyedArchiverBridge() }
public static var _remoteConfigArray: RemoteConfigKeyedArchiverArrayBridge<[Self]> { RemoteConfigKeyedArchiverArrayBridge() }
}
extension Dictionary: RemoteConfigSerializable where Key == String {
public typealias T = [Key: Value]
public typealias Bridge = RemoteConfigObjectBridge<T>
public typealias ArrayBridge = RemoteConfigArrayBridge<[T]>
public static var _remoteConfig: Bridge { Bridge() }
public static var _remoteConfigArray: ArrayBridge { ArrayBridge() }
}
extension Array: RemoteConfigSerializable where Element: RemoteConfigSerializable {
public typealias T = [Element.T]
public typealias Bridge = Element.ArrayBridge
public typealias ArrayBridge = RemoteConfigObjectBridge<[T]>
public static var _remoteConfig: Bridge { Element._remoteConfigArray }
public static var _remoteConfigArray: ArrayBridge {
fatalError("Multidimensional arrays are not supported yet")
}
}
extension Optional: RemoteConfigSerializable where Wrapped: RemoteConfigSerializable {
public typealias Bridge = RemoteConfigOptionalBridge<Wrapped.Bridge>
public typealias ArrayBridge = RemoteConfigOptionalBridge<Wrapped.ArrayBridge>
public static var _remoteConfig: Bridge { RemoteConfigOptionalBridge(bridge: Wrapped._remoteConfig) }
public static var _remoteConfigArray: ArrayBridge { RemoteConfigOptionalBridge(bridge: Wrapped._remoteConfigArray) }
}
只需一步即可开始使用 SwiftyRemoteConfig。
定义您的键!
extension RemoteConfigKeys {
var recommendedAppVersion: RemoteConfigKey<String?> { .init("recommendedAppVersion")}
var isEnableExtendedFeature: RemoteConfigKey<Bool> { .init("isEnableExtendedFeature", defaultValue: false) }
}
...然后就可以使用了!
// get remote config value easily
let recommendedVersion = RemoteConfigs[.recommendedAppVersion]
// eality work with custom deserialized types
let themaColor: UIColor = RemoteConfigs[.themaColor]
如果您使用 Swift 5.1 或更高版本,您还可以使用 keyPath dynamicMemberLookup
let subColor: UIColor = RemoteConfigs.subColor
要充分利用 SwiftyRemoteConfig,请提前定义您的远程配置键
let flag = RemoteConfigKey<Bool>("flag", defaultValue: false)
只需创建一个 RemoteConfigKey 对象。 如果您想要一个非可选的值,只需在键中提供一个 defaultValue(请看上面的例子)。
您现在可以使用 RemoteConfig 快捷方式来访问这些值
RemoteConfigs[key: flag] // => false, type as "Bool"
编译器不会让您获取便捷地返回 Bool。
为了更加方便,请通过扩展 magic RemoteConfigKeys 类并添加静态属性来定义您的键
extension RemoteConfigKeys {
var flag: RemoteConfigKey<Bool> { .init("flag", defaultValue: false) }
var userSectionName: RemoteConfigKey<String?> { .init("default") }
}
并使用快捷点语法
RemoteConfigs[\.flag] // => false
SwiftyRemoteConfig 支持以下标准类型
| 单个值 | 数组 |
|---|---|
String(字符串) |
[String](字符串数组) |
Int(整数) |
[Int](整数数组) |
Double(双精度浮点数) |
[Double](双精度浮点数数组) |
Bool(布尔值) |
[Bool](布尔值数组) |
Data(数据) |
[Data](数据数组) |
Date(日期) |
[Date](日期数组) |
URL |
[URL](URL 数组) |
[String: Any](字符串到 Any 类型的字典) |
[[String: Any]](字符串到 Any 类型字典的数组) |
但这还不是全部!
SwiftyRemoteConfig 支持 Codable! 只需在您的类型中遵循 RemoteConfigSerializable
final class UserSection: Codable, RemoteConfigSerializable {
let name: String
}
无需实现! 这样做您可以选择指定可选的 RemoteConfigKey
let userSection = RemoteConfigKey<UserSection?>("userSection")
此外,您还将免费获得数组支持
let userSections = RemoteConfigKey<[UserSection]?>("userSections")
像支持 Codable 一样支持您的自定义 NSCoding 类型
final class UserSection: NSObject, NSCoding, RemoteConfigSerializable {
...
}
最后,RawRepresentable 支持! 同样,情况与 Codable 和 NSCoding 相同
enum UserSection: String, RemoteConfigSerializable {
case Basic
case Royal
}
如果您想添加我们尚未支持的自己的自定义类型,我们已经为您考虑到了。 我们使用多种 RemoteConfigBridge 来指定您如何获取值和值数组。 当您查看 RemoteConfigSerializable 协议时,它期望每种类型中都有两个属性:_remoteConfig 和 _remoteConfigArray,两者都属于 RemoteConfigBridge 类型。
例如,这是使用 NSKeyedUnarchiver 进行单值数据检索的桥接
public struct RemoteConfigKeyedArchiveBridge<T>: RemoteConfigBridge {
public func get(key: String, remoteConfig: RemoteConfig) -> T? {
remoteConfig.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T
}
public func deserialize(_ object: RemoteConfigValue) -> T? {
guard let data = object as? Data else {
return nil
}
NSKeyedUnarchiver.unarchiveObject(with: data)
}
}
用于默认检索数组值的桥接
public struct RemoteConfigArrayBridge<T: Collection>: RemoteConfigBridge {
public func get(key: String, remoteConfig: RemoteConfig) -> T? {
remoteConfig.array(forKey: key) as? T
}
public func deserialize(_ object: RemoteConfigValue) -> T? {
return nil
}
}
现在,要在您的类型中使用这些桥接,您只需如下声明它
struct CustomSerializable: RemoteConfigSerializable {
static var _remoteConfig: RemoteConfigBridge<CustomSerializable> { RemoteConfigKeyedArchiverBridge() }
static var _remoteConfigArray: RemoteConfigBridge<[CustomSerializable]> { RemoteConfigKeyedArchiverBridge() }
let key: String
}
不幸的是,如果您发现自己需要自定义桥接器,您可能需要自己编写一个
final class RemoteConfigCustomBridge: RemoteConfigBridge {
func get(key: String, remoteConfig: RemoteConfig) -> RemoteConfigCustomSerializable? {
let value = remoteConfig.string(forKey: key)
return value.map(RemoteConfigCustomSerializable.init)
}
func deserialize(_ object: Any) -> RemoteConfigCustomSerializable? {
guard let value = object as? String {
return nil
}
return RemoteConfigCustomSerializable(value: value)
}
}
final class RemoteConfigCustomArrayBridge: RemoteConfigBridge {
func get(key: String, remoteConfig: RemoteConfig) -> [RemoteConfigCustomSerializable]? {
remoteConfig.array(forKey: key)?
.compactMap({ $0 as? String })
.map(RemoteConfigCustomSerializable.init)
}
func deserialize(_ object: Any) -> [RemoteConfigCustomSerializable]? {
guard let values as? [String] else {
return nil
}
return values.map({ RemoteConfigCustomSerializable.init })
}
}
struct RemoteConfigCustomSerializable: RemoteConfigSerializable, Equatable {
static var _remoteConfig: RemoteConfigCustomBridge { RemoteConfigCustomBridge() }
static var _remoteConfigArrray: RemoteConfigCustomArrayBridge: { RemoteConfigCustomArrayBridge() }
let value: String
}
为了支持具有不同桥接器的现有类型,您可以类似地扩展它
extension Data: RemoteConfigSerializable {
public static var _remoteConfigArray: RemoteConfigArrayBridge<[T]> { RemoteConfigArrayBridge() }
public static var _remoteConfig: RemoteConfigBridge<T> { RemoteConfigBridge() }
}d
另外,请查看我们的源代码或测试,以查看更多桥接器的示例。 如果您发现自己对所有这些桥接器感到困惑,请创建一个 issue,我们将解决一些问题。
SwiftyRemoteConfig 为 Swift 5.1 提供了属性包装器! 属性包装器 @SwiftyRemoteConfig 提供了一个选项,可以使用 key path。
注意:此属性包装器仅支持 read。 您可以将新值设置为属性,但是任何更改都不会反映到远程配置值
给定键
extension RemoteConfigKeys {
var userColorScheme: RemoteConfigKey<String> { .init("userColorScheme", defaultValue: "default") }
}
您可以声明一个 Settings 结构
struct Settings {
@SwiftyRemoteConfig(keyPath: \.userColorScheme)
var userColorScheme: String
}
您还可以使用 projected value 检查属性详细信息
struct Settings {
@SwiftyRemoteConfig(keyPath: \.newFeatureAvailable)
var newFeatureAvailable: String
}
struct NewFeatureRouter {
func show(with settings: Settings) {
if settings.$newFeatureAvailable.lastFetchTime != nil {
// show new feature
} else {
// fetch and activate remote config before routing
}
}
}
SwiftyRemoteConfig 使 KeyPath dynamicMemberLookup 可以在 Swift 5.1 中使用。
extension RemoteConfigKeys {
var recommendedAppVersion: RemoteConfigKey<String?> { .init("recommendedAppVersion")}
var themaColor: RemoteConfigKey<UIColor> { .init("themaColor", defaultValue: .white) }
}
然后就可以使用了 ;-)
// get remote config value easily
let recommendedVersion = RemoteConfig.recommendedAppVersion
// eality work with custom deserialized types
let themaColor: UIColor = RemoteConfig.themaColor
SwiftyRemoteConfig 通过 Combine 的流提供来自 RemoteConfig 的值。
extension RemoteConfigKeys {
var contentText: RemoteConfigKey<String> { .init("content_text", defaultValue: "Hello, World!!") }
}
并从 Combine 流中获取 RemoteConfig 的值!
import FirebaseRemoteConfig
import SwiftyRemoteConfig
import Combine
final class ViewModel: ObservableObject {
@Published var contentText: String
private var cancellables: Set<AnyCancellable> = []
init() {
contentText = RemoteConfigs.contentText
RemoteConfig.remoteConfig()
.combine
.fetchedPublisher(for: \.contentText)
.receive(on: RunLoop.main)
.assign(to: \.contentText, on: self)
.store(in: &cancellables)
}
}
如果您使用的是 Cocoapods,只需将此行添加到您的 Podfile
pod 'SwiftyRemoteConfig`, `~> 1.0.0`
通过在您的终端中运行此命令进行安装
$ pod install
然后在您使用它的所有文件中导入该库
import SwiftyRemoteConfig
只需添加您的 Cartfile
github "fumito-ito/SwiftyRemoteConfig" ~> 1.0.0
只需在您的 Package.swift 的 dependencies 下添加
let package = Package(
name: "MyPackage",
products: [...],
dependencies: [
.package(url: "https://github.com/fumito-ito/SwiftyRemoteConfig.git", .upToNextMajor(from: "1.0.0"))
]
)
SwiftyRemoteConfig 在 Apache License 2.0 下可用。 有关更多详细信息,请参见 LICENSE 文件。