JustTweak是iOS应用程序的功能标记框架。它提供了一个简单的外墙接口,与多个调整的提供商相互作用,这些提供程序尊重给定的优先级。调整代表用于在客户端代码中驱动决策的标志。
使用JustTweak,您可以实现以下各个方面:
JustTweak可以通过Cocoapods获得。要安装它,只需将以下行添加到您的podfile:
pod "JustTweak"
JustTweak也可以通过SPM获得。复制此存储库的URL,然后在项目设置中添加包装。
LocalTweakProvider
JSON文件,包括您的功能。有关起点,请参阅LocalTweaks_example.json
。要配置堆栈,您有两个选项:
static let tweakManager : TweakManager = {
var tweakProviders : [ TweakProvider ] = [ ]
// Mutable TweakProvider (to override tweaks from other TweakProviders)
let userDefaultsTweakProvider = UserDefaultsTweakProvider ( userDefaults : UserDefaults . standard )
tweakProviders . append ( userDefaultsTweakProvider )
// Optimizely (remote TweakProvider)
let optimizelyTweakProvider = OptimizelyTweakProvider ( )
optimizelyTweakProvider . userId = UUID ( ) . uuidString
tweakProviders . append ( optimizelyTweakProvider )
// Firebase Remote Config (remote TweakProvider)
let firebaseTweakProvider = FirebaseTweakProvider ( )
tweakProviders . append ( firebaseTweakProvider )
// Local JSON-based TweakProvider (default TweakProvider)
let jsonFileURL = Bundle . main . url ( forResource : " LocalTweaks_example " , withExtension : " json " ) !
let localTweakProvider = LocalTweakProvider ( jsonURL : jsonFileURL )
tweakProviders . append ( localTweakProvider )
return TweakManager ( tweakProviders : tweakProviders )
} ( )
LocalTweakProvider
支持的功能实现属性和常数。有关起点,请参阅TweakAccessor.swift
。 config.json
文件中定义堆栈配置: {
"accessorName" : " GeneratedTweakAccessor "
}
当前唯一支持的值是定义生成类的名称的accessorName
。
Podfile
script_phase :name = > ' TweakAccessorGenerator ' ,
:script = > ' $SRCROOT/../TweakAccessorGenerator
-l $SRCROOT/<path_to_the_local_tweaks_json_file>
-o $SRCROOT/<path_to_the_output_folder_for_the_generated_code>
-c $SRCROOT/<path_to_the_folder_containing_config.json> ' ,
:execution_position = > :before_compile
每次构建目标时,代码生成器工具都会重新生成堆栈的代码。它将包含备份LocalTweakProvider
中定义的功能的所有属性。
如果您使用了代码生成器工具,则生成的堆栈包括所有功能标志。只需分配访问器对象(您在.json
配置中定义的名称,然后使用它来访问功能标志。
let accessor = GeneratedTweakAccessor ( with : < #tweak_manager_instance# > )
if accessor . meaningOfLife == 42 {
...
}
有关生成的代码的示例,请参见GeneratedTweakAccessor.swift
和GeneratedTweakAccessor+Constants.swift
。
如果您决定自己实现堆栈代码,则必须实现通过TweakManager
访问功能的代码。
可以从TweakManager
实例访问JustTweak的三个主要功能来驱动代码路径决策。
// check for a feature to be enabled
let enabled = tweakManager . isFeatureEnabled ( " some_feature " )
if enabled {
// enable the feature
} else {
// default behaviour
}
TweakManager
将从优先级最高的调整提供商返回该值,如果找不到设定值,则会自动退回给其他值。当发现零调整时,它可以根据需要捕获和处理。使用tweakWith(feature:variable:)
或提供的属性包装器。
// check for a tweak value
let tweak = try ? tweakManager . tweakWith ( feature : " some_feature " , variable : " some_flag " )
if let tweak = tweak {
// tweak was found in some tweak provider, use tweak.value
} else {
// tweak was not found in any tweak provider
}
或带有do-catch
// check for a tweak value
do {
let tweak = try tweakManager . tweakWith ( feature : " some_feature " , variable : " some_flag " )
// tweak was found in some tweak provider, use tweak.value
return tweak
} catch let error as TweakError {
switch error {
case . notFound : ( ) // "Feature or variable is not found"
case . notSupported : ( ) // "Variable type is not supported"
case . decryptionClosureNotProvided : ( ) // "Value is encrypted but there's no decryption closure provided"
}
} catch let error { // add a default catch to satisfy the compiler
print ( error . localizedDescription )
}
@TweakProperty
, @OptionalTweakProperty
和@FallbackTweakProperty
属性包装器可用于标记代表功能标志的属性。请注意,为了使用这些属性包装器,需要一个TweakManager
的静态实例。
@ TweakProperty ( feature : < #feature_key# > ,
variable : < # var iable_key# > ,
tweakManager : < #TweakManager# > )
var labelText : String
@ OptionalTweakProperty ( fallbackValue : < #nillable_fallback_value# > ,
feature : < #feature_key# > ,
variable : < # var iable_key# > ,
tweakManager : < #TweakManager# > )
var meaningOfLife : Int ?
@ FallbackTweakProperty ( fallbackValue : < #nillable_fallback_value# > ,
feature : < #feature_key# > ,
variable : < # var iable_key# > ,
tweakManager : < #TweakManager# > )
var shouldShowFeatureX : Bool
tweakProviders
数组中对象的顺序定义了调整提供商的优先级。
具有最高优先级的MutableTweakProvider
,例如上面示例中的UserDefaultsTweakProvider
,将用于反映UI中所做的更改( TweakViewController
)。 LocalTweakProvider
应该具有最低的优先级,因为它提供了本地调整提供商的默认值,并且是TweakViewController
使用的默认值。
为了从手册迁移到代码生成的实现,有必要更新为新的.json
格式。为了协助此过程,我们已将GeneratedPropertyName
属性添加到调整对象中。设置此值以与代码中的当前属性名称保持一致,以使生成的访问属性匹配您现有的实现。
TweakManager
提供了缓存调整值以提高性能的选项。默认情况下禁用缓存,但可以通过useCache
属性启用。启用后,有两种重置缓存的方法:
TweakManager
上调用resetCache
方法TweakProviderDidChangeNotification
通知JustTweak带有一个ViewController,该视图允许用户编辑具有最高优先级的MutableTweakProvider
。
func presentTweakViewController ( ) {
let tweakViewController = TweakViewController ( style : . grouped , tweakManager : < #TweakManager# > )
// either present it modally
let tweaksNavigationController = UINavigationController ( rootViewController : tweakViewController )
tweaksNavigationController . navigationBar . prefersLargeTitles = true
present ( tweaksNavigationController , animated : true , completion : nil )
// or push it on an existing UINavigationController
navigationController ? . pushViewController ( tweakViewController , animated : true )
}
当在任何MutableTweakProvider
中修改值时,发出通知,以使客户有机会反应并反映UI的变化。
override func viewDidLoad ( ) {
super . viewDidLoad ( )
NotificationCenter . defaultCenter ( ) . addObserver ( self ,
selector : #selector ( updateUI ) ,
name : TweakProviderDidChangeNotification ,
object : nil )
}
@ objc func updateUI ( ) {
// update the UI accordingly
}
JustTweak带有三个开箱即用的调整提供商:
UserDefaultsTweakProvider
是可变的,并使用UserDefaults
用作密钥/值存储LocalTweakProvider
,它是仅读取的,并使用旨在保存默认功能标记设置的JSON文件EphemeralTweakProvider
,这只是NSMutableDictionary
的实例此外,JustTweak定义了TweakProvider
和MutableTweakProvider
协议,您可以实现以创建自己的调整提供商以满足您的需求。在示例项目中,您可以找到一些可以用作起点的示例。
JustTweak提供了在TweakProvider
中添加decryptionClosure
的能力。此封闭将Tweak
作为输入,并返回一个TweakValue
作为输出。闭合使您可以对调整进行一些预处理,例如可以使用来解密值。如果您在调整JSON文件中具有加密值,则可以使用此方法,如下所示:
"encrypted_answer_to_the_universe" : {
"Title" : " Encrypted definitive answer " ,
"Description" : " Encrypted answer to the Ultimate Question of Life, the Universe, and Everything " ,
"Group" : " General " ,
"Value" : " 24 ton yletinifeD " ,
"GeneratedPropertyName" : " definitiveAnswerEncrypted " ,
"Encrypted" : true
}
请注意,您必须指定该值是否在JSON文件(带有Encrypted
属性)中加密,以进行解密闭合以处理该值。上述JSON的解密闭合可以指定如下:
tweakProvider . decryptionClosure = { tweak in
// decrypt `tweak.value` with your cypher of choice and return the decrypted value
}
这样,从调整提供商那里获取的调整将具有解密的价值。
JustTweak可在Apache 2.0许可下使用。有关更多信息,请参见许可证文件。