Swift中的feature flags
在为应用开发新功能时,采用某种形式的机制逐步推出新的实现和功能可能非常有用,而不必一次向每个用户启动。 这不仅可以帮助“降低”重大更改的启动风险(如果出现问题,我们可以随时回滚),还可以帮助我们收集有关部分完成的功能的反馈,或使用A / B等技术进行实验 测试。
功能标志可以充当这种机制。 从本质上讲,它们使我们可以在编译时或运行时在某些条件下关闭代码库的某些部分。本文让我们看一下在Swift中可以使用功能标记的几种不同方式。
Conditional compilation 条件编译
处理代码库时,在处理正在进行的功能时,可以使用多种策略。例如,我们可以使用诸如功能分支之类的东西,并使用版本控制来使正在开发的功能与我们的主master分支完全分开。准备发布该功能后,我们只需将其合并并发布。
但是,将新的实现和功能连续集成到我们的主分支中有一些巨大的优势。它使我们能够及早发现错误和问题,如果两个分支之间的分歧很大,则可以免去解决大量合并冲突的痛苦,还可以让我们在内部或内部发布新功能的预发布版本。到Beta测试人员。
但是我们仍然需要一些方法来删除不应实时发布到App Store的代码。一种方法是使用编译器标志,它使我们可以标记代码块,以便仅在设置了标志的情况下才将其编译。假设我们的应用当前正在使用Core Data,并且我们希望在生产中保持这种方式(目前),同时仍然能够尝试一种新的解决方案-Realm。为此,我们可以使用DATABASE_REALM编译器标志,该标志仅用于我们要在其中使用Realm的内部版本(例如,对于beta内部版本)。然后,我们可以告诉编译器在构建应用程序时检查该标志,如下所示:
class DataBaseFactory {
func makeDatabase() -> Database {
#if DATABASE_REALM
return RealmDatabase()
#else
return CoreDataDatabase()
#endif
}
}
要打开或关闭上述标志,我们可以简单地在Xcode中打开目标的构建设置,然后在Swift编译器-自定义标志>活动编译条件下添加或删除DATABASE_REALM。 这对于仍处于活动开发中的功能特别有用,这使使用该功能的开发人员可以轻松地在本地将其打开,而不会影响生产版本。
静态标志
当您想从应用程序中完全删除新实现的代码时,条件编译非常有用。 但是有时候这不是必需的,就是不实际的,在那些情况下,在代码中定义功能标记可能是一个更好的选择。
一种非常简单的方法是使用静态属性。 例如,我们可以创建一个包含所有标志的FeatureFlags结构,如下所示:
struct FeatureFlags {
static let searchEnabled = false
static let maximumNumberOfFavorites = 10
static let allowLandscapeMode = true
}
有了上面的FeatureFlags类型,我们现在可以将检查放在激活给定功能的代码路径中。 这是一个有条件地激活搜索功能的示例方法,该功能从ListViewController的viewDidLoad()方法中调用:
extension ListViewController {
func addSearchIfNeeded() {
// If the search feature shouldn't be enabled, we simply return
guard FeatureFlags.searchEnabled else {
return
}
let resultsVC = SearchResultsViewController()
let searchVC = UISearchController(
searchResultsController: resultsVC
)
searchVC.searchResultsUpdater = resultsVC
navigationItem.searchController = searchVC
}
}
静态标志的好处在于,它们与编译器标志一样,非常容易设置和集成。但是,它们不允许我们在应用程序编译后修改标志的值。为了做到这一点,我们需要开始使用运行时标志。
运行时标志
添加选项以在运行时配置我们的应用程序的功能标志可能有点“双刃剑”。一方面,它可以使我们为特定比例的用户群更改给定标志的值,从而使我们能够执行A / B测试;另一方面,它可以使我们的应用更难以维护和调试-因为最终使用的代码路径在编译时尚未完全确定。
运行时标志通常是从某种形式的后端系统加载的,并且有可能(取决于应用程序的体系结构)甚至包含在应用程序作为用户登录的一部分而收到的响应中(否则,通常有一个/ feature_flags端点或类似的标志)该应用在启动时进行查询)。 (可选)我们还可以使用某种形式的调试UI启用在应用程序本身中调整标志的功能。
无论我们如何加载功能标记的值,我们都希望更新FeatureFlags类型以使用实例属性而不是静态属性。这样,我们可以加载标志的值,然后将其转换为FeatureFlags实例,然后在需要时将其注入。我们的标志类型现在看起来像这样:
struct FeatureFlags {
let searchEnabled: Bool
let maximumNumberOfFavorites: Int
let allowLandscapeMode: Bool
}
为了能够从序列化格式转换实例,我们还将添加带有字典的初始化程序。 这样,我们可以从JSON后端响应或从应用程序本地存储的值(例如,从缓存)创建功能标志:
extension FeatureFlags {
init(dictionary: [String : Any]) {
searchEnabled = dictionary.value(for: "search", default: false)
maximumNumberOfFavorites = dictionary.value(for: "favorites", default: 10)
allowLandscapeMode = dictionary.value(for: "landscape", default: true)
}
}
private extension Dictionary where Key == String {
func value<V>(for key: Key,
default defaultExpression: @autoclosure () -> V) -> V {
return (self[key] as? V) ?? defaultExpression()
}
}
现在,我们可以在应用启动或用户登录时加载功能标志(取决于我们是否希望我们的标志是特定于用户的),并在需要时注入它们,如下所示:
class FavoritesManager {
private let featureFlags: FeatureFlags
init(featureFlags: FeatureFlags) {
self.featureFlags = featureFlags
}
func canUserAddMoreFavorites(_ user: User) -> Bool {
let maxCount = featureFlags.maximumNumberOfFavorites
return user.favorites.count < maxCount
}
}
现在,我们可以自由切换某些功能的打开或关闭,或调整确定应用程序逻辑一部分的值。 我们还可以在我们的应用程序运行时使我们的标志发生变化(并添加某种形式的观察API来对更改做出反应),但是我个人很少认为增加这么多的复杂性值得。 只需加载一次值并在其后设置应用程序,这将使事情变得简单,并避免引入棘手的极端情况。
结论
能够快速迭代应用程序时,使用功能标志可能是关键,尤其是随着应用程序团队的不断壮大以及代码库的更新速度大大提高。通过能够有条件地启用某些功能或调整它们的行为,我们通常可以更快地将代码集成到我们的主分支中,并且仍然继续交付我们的应用程序。
功能标志也可能给我们的设置增加一些复杂性,尤其是在使用运行时标志时。重现仅在标志的特定组合处于启用状态时才发生的错误会变得更加困难,并且测试我们应用程序的所有潜在代码路径可能很快变得更加复杂且耗时。
如果您以前从未使用过功能标志,我建议您从简单开始(也许使用编译器标志或静态标志),然后从那里开始。与大多数工具一样,它们可能会要求您稍微采用工作流程,尤其是在项目当前不使用太多自动化测试的情况下(这会使使用功能标志的风险大大降低)。
推荐
基础文章推荐
经典教程推荐
上新
技术源码推荐
推荐文章
CoreData篇
Combine篇
TextField篇
- 《SwiftUI 一篇文章全面掌握TextField文本框 (教程和全部源码)》
- 《SwiftUI实战之TextField风格自定义与formatters》
- 《SwiftUI实战之TextField如何给键盘增加个返回按钮(隐藏键盘)》
- 《SwiftUI 当键盘出现时避免TextField被遮挡自动向上移动》
- 《SwiftUI实战之TextField如何给键盘增加个返回按钮(隐藏键盘)》
JSON文件篇
一篇文章系列
- SwiftUI一篇文章全面掌握List(教程和源码)
- 《SwiftUI 一篇文章全面掌握TextField文本框 (教程和全部源码)》
- SwiftUI一篇文章全面掌握Picker,解决数据选择(教程和源码)
- SwiftUI一篇文章全面掌握Form(教程和源码)
- SwiftUI Color 颜色一篇文章全解决
技术交流
QQ:3365059189
SwiftUI技术交流QQ群:518696470
- 请关注我的专栏icloudend, SwiftUI教程与源码
https://www.jianshu.com/c/7b3e3b671970