注意:此工具需要 Xcode 11 才能编译,因为它使用了一些 Swift 5.1 语言特性。
SVMPrefs
是一个命令行工具,用于生成基于 SVM
数据读取和写入偏好设置的代码。
SVM
名称来自三个主要数据元素:Store(存储),Variable(变量)和 Migrate(迁移)。
通常使用 UserDefaults
的方式如下所示。
let prefs = UserDefaults.standard
if !prefs.bool(forKey: "firstLaunch") {
prefs.set(true, forKey: "firstLaunch") {
showFTUX()
}
这种临时使用 UserDefaults
的方式至少存在 11 个问题
UserDefaults.standard
"firstLaunch"
prefs.bool
和 true
!
和 true
(你注意到反转的逻辑了吗?)showFTUX()
。UserDefaults
位置并非易事上述问题的一种解决方案是定义一个专用类,该类封装每个偏好设置的详细信息,以便应用程序逻辑可以专注于以简单明了的方式使用它们。使用专用类,使用偏好设置可以像这样
let prefs = AppPrefs()
if prefs.isFirstLaunch {
prefs.isFirstLaunch = false
showFTUX()
}
SVMPrefs 通过根据您的 SVM 规范生成读取、写入、迁移和删除偏好设置的代码,更进一步。
注意:此工具需要 Xcode 11 才能编译,因为它使用了一些 Swift 5.1 语言特性。
从 SVMPrefs 根目录运行 make install
以构建并在 /usr/local/bin
中安装 svmprefs
二进制文件。
您可以使用 Xcode 11 通过打开 Package.swift
文件或从命令行使用 xed .
打开项目。
基本命令行如下:svmprefs [command] [options] [args]
命令 | 描述 |
---|---|
help |
显示帮助文本 |
version |
显示版本信息 |
gen source_file_name |
处理给定的文件并在 SVM 数据中生成代码 |
您可以运行 svmprefs gen --help
以获取有关 gen
命令的更多详细信息。
> svmprefs gen --help
Usage: svmprefs gen <input> [options]
Processes the given file and generates the code for the contained SVM data
Options:
-b, --backup Create a backup copy of the source file (foo.m -> foo.backup.m)
-d, --debug Print debug output
-h, --help Show help information
-i, --indent <value> Set indent width. (Default: 4)
您可以将 SVMPrefs 集成到您的 Xcode 项目中,使其在编译之前生成代码,并通过运行脚本突出显示 SVM 规范中的任何错误。
添加一个新的“Run Script Phase”,该阶段发生在编译之前,内容如下所示。
set -e
if which svmprefs >/dev/null; then
# Update this section with the desired command line options and
# actual file paths to your code that have SVM data.
# NOTE: svmprefs supports just one file at a time.
svmprefs gen -i 4 $SRCROOT/Common/SharedUserDefaults.swift
else
echo "WARNING: svmprefs is not installed. See: https://github.com/ghv/SVMPrefs"
fi
您必须在代码中添加一个注释块,该注释块以 SVMPREFS
开始和结束,如下所示。
/*SVMPREFS [NB: the rest of this line reserved for svmprefs tool use]
# this line is treated as a comment by svmprefs
S demo
V Bool | isDemo | demo_key_name | |
SVMPREFS*/
任何以 #
作为第一个非空白字符的行都被视为 SVMPREFS
注释块中的注释
存储记录有三个由 |
分隔的参数
name
- 此存储的名称,用于定义存储的类实例变量和代码标记标识符。除了 delete
和 migrate
之外,该名称可以是任何内容。suite
- 如果指定,则用于使用套件名称(在 iOS 中又名应用程序组)构造存储对象的表达式。请参阅 UserDefaults
。使用 none
可以省略生成存储变量,因为您将在您的类中提供一个。留空或写入 standard
以使用 UserDefaults.standard
。options
- 以逗号分隔的代码生成标志集。(请参阅下面的代码)enum Options: String {
case generateRemoveAllMethod = "RALL"
}
您为每个唯一的套件定义一个 S
记录。每个 S
记录后跟任意数量的 V
记录。
变量记录有五个由 |
分隔的参数
type
- 任何有效的变量类型表达式,包括数组、日期、可选值和字典。name
- 此偏好设置的属性名称。如果变量是布尔类型,则如果尚未添加 is
前缀,则将添加该前缀。key
- 偏好设置的键名。不支持前导和尾随空白字符。options
- 可选的以逗号分隔的代码生成标志集(请参阅下面的代码)default
- 如果偏好设置在存储中不存在或具有空值,则返回的可选默认值。enum Options: String {
case generateInvertedLogic = "INV"
case decorateWithObjC = "OBJC"
// Defining a Bool named 'firstLaunch' with this option will
// generate code for it as 'isFirstLaunch' in some places.
case decorateNameWithIsPrefix = "IS"
case omitGeneratingGetterAndSetter = "NVAR"
case omitGeneratingSetter = "NSET"
case addToRemoveAllMethod = "RALL"
// Use this to omit when RALL is set at the store level:
case omitFromRemoveAllMethod = "NRALL"
case generateRemovePrefMethod = "REM"
case generateIsSetMethod = "ISSET"
}
如果您有一个或多个 S
记录,则可以使用 M
记录将偏好设置从一个存储移动到另一个存储,或者根据需要删除它们。
迁移记录有四个由 |
分隔的参数
source store
- 源 S
记录的 name
destination store
- 目标 S
记录的 name
。如果正在删除源变量,则使用 delete
。source variable name
- 源存储中的变量 name
。destination variable name
- 目标存储中的变量 name
。如果正在删除,则省略。该工具会将所有迁移代码插入到一个名为 migrate()
的函数中,您应该在每次应用程序启动时调用该函数。迁移是作为对象到对象的读取和写入执行的。迁移后,该键将从源存储中删除。您必须在您的类中的某个位置包含一个名为 migrate
的代码标记。
任何被迁移或删除的变量都将无法再从源存储作为属性访问。但是,此属性的键将保留在源存储的 Keys
枚举中。
生成的代码必须放置在源文件中的一个类中。如下所示添加一对注释,其中 identifier
是存储的 name
,以指示要插入生成代码的位置。
// MARK: BEGIN identifier
// MARK: END identifier
如果您定义了任何迁移,您还需要包含一个用于 migrate
identifier
的代码标记。
您可以在同一个 MARK 中指定多个 identifier
,方法是将它们与逗号分隔符连接在一起,按照您希望它们出现的顺序排列。开始和结束标记必须具有相同标识符且顺序相同。逗号周围没有空格,否则您将收到错误。
// MARK: BEGIN foo,bar
// MARK: END foo,bar
/*SVMPREFS
S demo
V Bool | isDemo | demo_key_name | |
SVMPREFS*/
class MyDemoPreferences {
// ANYTHING HERE IS LEFT UNTOUCHED
// MARK: BEGIN demo
// ANYTHING HERE WILL BE REPLACED
// BY THE GENERATED CODE
// MARK: END demo
// ANYTHING HERE IS LEFT UNTOUCHED
}
在某些情况下,您可能需要在函数中提供对偏好设置的引用,以便其中的代码可以读取或写入该偏好设置,而不必知道实际的属性。如果您过去使用键名作为此引用,则现在可以使用 KeyPath
或 WriteableKeyPath
来实现此目的。这是一个例子。
/*SVMPREFS
S keypath
V [String] | primaryList | app.primaryList | |
V [String] | secondaryList | app.secondaryList | |
SVMPREFS*/
class KeyPathPrefs {
// MARK: BEGIN keypath
// MARK: END keypath
}
// Somewhere in your app...
func demo() {
// A function that needs to use one of several preferences
func processList(keyPath: KeyPath<KeyPathPrefs, [String]>) {
let prefs = KeyPathPrefs()
let list = prefs[keyPath: keyPath]
// Do something with this list...
}
// How you call it:
processList(keyPath: \.primaryList)
processList(keyPath: \.secondaryList)
}
版权所有 2019-2020 The SVMPrefs Authors。SVMPrefs 在 Apache 2.0 许可证下获得许可。欢迎贡献。
有关许可证信息,请参阅 LICENSE.md。
有关 The SVMPrefs Authors 的信息,请参阅 CONTRIBUTORS.md。
有关依赖项许可证信息,请参阅 NOTICE.md。