InAppSettingsKit (IASK) 是一个开源框架,可以轻松地将应用内设置添加到您的 iOS、Catalyst 或 visionOS 应用中。通常,iOS 应用使用 Settings.bundle
资源在“设置”应用中添加特定于应用的设置。InAppSettingsKit 利用相同的 bundle,并允许您在您的应用内呈现相同的设置屏幕。因此,用户可以选择在哪里更改设置。
IASK 不仅复制了系统设置的功能集,而且还支持大量额外的元素和配置选项。
从 IASK 2.x 更新? 请阅读发行说明。
为了支持传统的 Settings.app 面板,应用程序必须包含一个 Settings.bundle
,其中至少包含一个 Root.plist
,以指定设置 UI 元素与 NSUserDefaults
键的连接。 InAppSettingsKit 基本上只是使用相同的 Settings.bundle 来完成其工作。这意味着当您想要包含新的设置参数时,无需进行额外的工作。只需将其添加到 Settings.bundle,它就会同时出现在应用内和 Settings.app 中。支持所有设置类型,如文本字段、滑块、切换元素、子视图等。
源代码可在 github 上找到。 有几种安装方法。
使用 SPM
要使用 Swift Package Manager 安装 InAppSettingsKit,您可以按照 Apple 发布的教程,使用 InAppSettingsKit 仓库的 URL 和当前版本。
https://github.com/futuretap/InAppSettingsKit.git
使用 CocoaPods
添加到您的 Podfile
pod 'InAppSettingsKit'
然后运行 pod install
。
使用 Carthage
添加到您的 Cartfile
github "futuretap/InAppSettingsKit" "master"
InAppSettingsKit 包含一个 Xcode 示例应用程序,演示了其所有丰富的功能。 同时适用于推送和模态视图控制器。
要运行示例应用程序
InAppSettingsKit.xcworkspace
。Sample App
(Product > Scheme > Sample App)。为了开始使用 IASK,请将 Settings.bundle
添加到您的项目 (File
-> Add File
-> Settings bundle
) 并使用您的设置编辑 Root.plist
(请参阅 Apple 关于 Schema File Root Content 的文档)。 请继续阅读以深入了解更多高级用法。
要显示 InAppSettingsKit,请实例化 IASKAppSettingsViewController
并将其推送到导航堆栈上或将其嵌入为导航控制器的根视图控制器。
在代码中,使用 Swift
let appSettingsViewController = IASKAppSettingsViewController()
navigationController.pushViewController(appSettingsViewController, animated: true)
在代码中,使用 Swift 作为 swift 包的一部分
在模块化应用程序中,您可能希望将所有与设置相关的代码移动到一个单独的包中,并且仅在该处引用 InAppSettingsKit 依赖项。 您的 Package.swift
看起来像这样
let package = Package(
name: "SettingsPackage",
platforms: [.iOS(.v17)],
dependencies: [
.package(url: "https://github.com/futuretap/inappsettingskit", from: "3.4.0")
],
.target(
name: "SettingsPackage",
dependencies: [
.product(name: "InAppSettingsKit", package: "inappsettingskit"),
],
resources: [
.copy("InAppSettings.bundle")
]
)
)
(请注意,InAppSettings.bundle
目录也是包的一部分,不再属于主应用程序。)
现在创建一个 IASKAppSettingsViewController
需要将其 bundle
属性设置为包的 bundle
struct InAppSettingsView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
let iask = IASKAppSettingsViewController(style: .insetGrouped)
iask.bundle = Bundle.module // IMPORTANT
return iask
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
}
在代码中,使用 Objective-C
IASKAppSettingsViewController *appSettingsViewController = [[IASKAppSettingsViewController alloc] init];
[self.navigationController pushViewController:appSettingsViewController animated:YES];
通过情节提要
IASKAppSettingsViewController
IASKAppSettingsViewControllerDelegate
。-settingsViewControllerDidEnd:
并关闭视图控制器。示例应用程序展示了如何连接所有内容。
其他更改
要自定义行为,请实现 IASKSettingsDelegate
并设置 IASKAppSettingsViewController
的 delegate
属性。 对于高级自定义需求,支持 IASKAppSettingsViewController 的子类化。
根据您的项目,可能需要在应用程序的启动代码中进行一些更改。 如果用户更改了设置,您的应用程序必须能够在运行时重新配置自身。 这可以在 -reconfigure
方法中完成,该方法从 -applicationDidFinishLaunching
以及 IASKAppSettingsViewController
的代理方法 -settingsViewControllerDidEnd:
中调用。
InAppSettingsKit 的目的是创建 100% 模仿 Settings.app 的行为 (请参阅 Apple Settings Application Schema Reference)。 最重要的是,我们添加了大量奖励功能,使 IASK 更加灵活和动态。
设置 plists 可以依赖于设备:Root~ipad.plist
将在 iPad 上使用,而 Root~iphone.plist
将在 iPhone 上使用。 如果不存在,将使用 Root.plist
。
InAppSettingsKit 添加了通过使用 .inApp.plist
而不是 .plist
来覆盖这些标准文件的可能性。 或者,您可以创建一个完全独立的 bundle,名为 InAppSettings.bundle
而不是通常的 Settings.bundle
。 如果您想禁止在 Settings.app 中显示设置,后一种方法很有用。
这是 plists 的完整搜索顺序
如果应用程序在其 Info.plist
中包含用于各种隐私功能(例如相机或位置访问)的使用密钥,则 IASK 会在根设置页面的顶部显示一个“隐私”单元格。 此单元格打开系统“设置”应用,并显示应用程序的设置面板,用户可以在其中指定应用程序的隐私设置。
如果您不想显示“隐私”单元格,请将属性 neverShowPrivacySettings
设置为 YES
。
示例应用程序定义了 NSMicrophoneUsageDescription
以显示单元格。 请注意,设置页面尚未显示任何隐私设置,因为应用程序实际上并未访问麦克风。 隐私设置仅在首次使用受隐私保护的 API 后才会在“设置”应用中显示。
您可以通过在 IASKAppSettingsViewController 实例的视图上设置 tintColor
来指定它。 颜色用于按钮和居中文字 (请参阅下文)。 可选地,您可以指定 settingsViewController.colorScheme = IASKColorSchemeTinted
以将 tintColor 用于所有用户可编辑的选项,例如多值元素。
InAppSettingsKit 添加了一个新元素 IASKOpenURLSpecifier
,允许使用外部应用程序(即 Safari 或 Mail)打开指定的 URL。 要启动的 URL 在 File
参数中指定。 有关详细信息,请参见示例 Root.inApp.plist
。
要在您的应用程序内打开指定的 URL,IASKAppSettingsWebViewController
会显示一个全屏视图控制器,其中嵌入了 WKWebView
。
默认情况下,它会在页面加载时在导航栏的右侧显示一个不确定的活动指示器。
Web 视图控制器可以在 Settings plist 中定义,方法是使用以下强制属性
Type
:设置为 PSChildPaneSpecifier
IASKViewControllerClass
:设置为 IASKAppSettingsWebViewController
IASKViewControllerSelector
:设置为 initWithFile:specifier:
Title
:行的本地化标题File
:对应于您要加载的 URL (例如 "https://www.futuretap.com")使用以下可选属性来自定义 Web 视图控制器
IASKWebViewShowProgress
:设置为 YES
以将导航栏上的默认活动指示器替换为导航栏正下方的进度条,该进度条根据 WKWebView
的 estimatedProgress
属性动态更新。IASKWebViewShowNavigationalButtons
:设置为 YES
以在导航栏的右侧显示导航按钮。 它们启用状态将根据 WKWebView
的导航历史记录动态更新。IASKWebViewHideBottomBar
:设置为 YES
以在 IASKAppSettingsWebViewController
推送到导航控制器上时隐藏屏幕底部的标签栏。 这将呈现 WKWebView
全屏显示,并防止用户在 IASKAppSettingsWebViewController
仍然存在的情况下导航标签栏的情况。IASKAppSettingsWebViewController
时,将忽略此设置。有关更多详细信息,请打开示例应用程序并查看所有以 WebView 开头的行。
尽管 IASKAppSettingsWebViewController
看上去可能与 SFSafariViewController
相似,但最大的区别在于 IASKAppSettingsWebViewController
不会向用户显示 URL,也不能在外部浏览器(即 Safari 或 Chrome)中打开。
换句话说,它可以保护您的源代码的私密性。
自定义的 IASKMailComposeSpecifier
元素允许通过打开邮件撰写视图从应用程序内发送邮件。您可以使用设置 plist 设置以下(可选)参数:IASKMailComposeToRecipents
、IASKMailComposeCcRecipents
、IASKMailComposeBccRecipents
、IASKMailComposeSubject
、IASKMailComposeBody
、IASKMailComposeBodyIsHTML
。可选地,您可以实现
- (BOOL)settingsViewController:(id<IASKViewController>)settingsViewController shouldPresentMailComposeViewController:(MFMailComposeViewController*)mailComposeViewController forSpecifier:(IASKSpecifier*)specifier;
在您的代理中,您可以自定义邮件(例如,预先填充带有动态内容的正文、添加附件)、修改撰写视图控制器的外观,甚至阻止标准呈现。如果设备上未配置电子邮件,则会显示警报。 IASKSpecifier
是定义单个设置单元格的内部模型对象。重要的 IASKSpecifier 属性包括:
key
: 对应于设置 plist 中的 Key
title
: 设置键的本地化标题type
: 对应于设置 plist 中的 Type
defaultValue
: 对应于设置 plist 中的 DefaultValue
InAppSettingsKit 添加了一个 IASKButtonSpecifier
元素,允许调用自定义操作。只需添加以下代理方法
- (void)settingsViewController:(IASKAppSettingsViewController*)sender buttonTappedForSpecifier:(IASKSpecifier*)specifier;
发送者始终是 IASKAppSettingsViewController
的实例,它是 UIViewController
的子类。因此,您可以访问其视图属性(可能便于显示操作表)或推送另一个视图控制器。另一个巧妙的功能是,IASK 按钮的标题可以被 NSUserDefaults
(或任何其他设置存储 - 见下文)中的(可本地化的)值覆盖。这对于切换按钮(例如,登录/注销)非常方便。有关详细信息,请参见示例应用程序。
默认情况下,按钮居中对齐,除非指定了图像(默认:左对齐)。默认对齐方式可能会被覆盖。
与标准文本字段类似,IASKTextViewSpecifier
显示一个全宽、多行文本视图,该视图会根据输入的文本调整大小。它还支持 KeyboardType
、AutocapitalizationType
和 AutocorrectionType
。
IASKDatePickerSpecifier
显示一个 UIDatePicker
以设置日期和/或时间。它支持以下选项:
DatePickerMode
:Date
、Time
或 DateAndTime
中的一个(请参阅 UIDatePickerMode)。默认值为 DateAndTime
。DatePickerStyle
:Compact
、Wheels
或 Inline
中的一个(请参阅 UIDatePickerStyle)。默认值为 Wheels
。此功能需要 iOS 14 或更高版本。如果操作系统不支持,IASK 会回退到 Wheels
。MinuteInterval
:日期选择器显示分钟的间隔。默认值:1。有 3 个可选的代理方法可以自定义如何存储和显示日期和时间:
- (NSDate*)settingsViewController:(IASKAppSettingsViewController*)sender dateForSpecifier:(IASKSpecifier*)specifier;
如果您以 NSDate
对象以外的自定义格式存储日期/时间,请实现此方法。当用户通过选择日期/时间选择器上方的标题单元格来开始编辑日期/时间时调用。
- (NSString*)settingsViewController:(IASKAppSettingsViewController*)sender datePickerTitleForSpecifier:(IASKSpecifier*)specifier;
实现此方法以自定义日期/时间选择器上方的标题单元格中显示的值。
- (void)settingsViewController:(IASKAppSettingsViewController*)sender setDate:(NSDate*)date forSpecifier:(IASKSpecifier*)specifier;
如果您以 NSDate
对象以外的自定义格式存储日期/时间,请实现此方法。当用户使用选择器更改日期/时间值时调用。
列表组 (IASKListGroupSpecifier
) 是一项仅限 IASK 的功能,允许您管理可变数量的项目,包括添加和删除项目。标签、帐户、名称数组是典型的用例。列表组由可变数量的 ItemSpecifier
项目组成。这些项目的数量由 NSUserDefaults(或您的自定义设置存储)中的实际内容决定。换句话说,ItemSpecifier
定义单元格的类型,而单元格的数量及其内容来自 NSUserDefaults 或您的存储。如果 Deletable
参数设置为 YES,则可以通过滑动删除单元格。
可选地,列表组还有一个 AddSpecifier
,用于控制列表组部分的最后一个项目。它用于添加项目,可以是文本字段、切换、滑块或子窗格。前三个在编辑完成后创建一个新项目,而子窗格则呈现一个模态子视图控制器来配置一个复杂项目,并将其保存为字典。这种子窗格的工作方式与普通的子窗格非常相似,但有一些区别:它们不是通过推送而是以模态方式呈现,并且在导航栏中具有“取消”和“完成”按钮。通过点击“完成”按钮创建一个新项目。
您可能需要指定一些在启用“完成”按钮之前需要满足的验证规则。这可以通过代理方法来实现:
- (BOOL)settingsViewController:childPaneIsValidForSpecifier:contentDictionary:
当从此方法返回 false 时,“完成”按钮将被禁用。另请注意,contentDictionary
是一个可变字典。如果您更改某些值,UI 将反映这一点。这允许您自动更正无效设置。
您可以使用类型 IASKCustomViewSpecifier
在 InAppSettingsKit 中指定自己的 UITableViewCell
。在这种情况下,强制性字段是 Key
属性。此外,您必须支持 IASKSettingsDelegate
协议并实现以下方法:
- (CGFloat)settingsViewController:(UITableViewController<IASKViewController> *)settingsViewController heightForSpecifier:(IASKSpecifier *)specifier;
- (UITableViewCell*)settingsViewController:(UITableViewController<IASKViewController> *)settingsViewController cellForSpecifier:(IASKSpecifier*)specifier;
这两个方法都会为您的所有 IASKCustomViewSpecifier
条目调用。要区分它们,您可以使用 specifier.key
访问 Key
属性。在第一个方法中,您返回单元格的高度,在第二个方法中,返回单元格本身。您应该像在表视图编程中一样使用可重用的 UITableViewCell
对象。演示应用程序中有一个示例。
可选地,您可以实现
- (void)settingsViewController:(IASKAppSettingsViewController*)settingsViewController didSelectCustomViewSpecifier:(IASKSpecifier*)specifier;
捕获自定义视图的点击事件。
如果您指定 File
、IASKViewControllerClass
、IASKViewControllerStoryBoardId
或 IASKSegueIdentifier
(见下文),则自定义视图的选择行为与子窗格相同,并且在选择时不调用委托。
Group 元素的 FooterText 键在系统设置中可用。InAppSettingsKit 也支持它。最重要的是,我们也支持 Multi Value 元素的这个键。页脚文本显示在多值选项表下方。
您可以通过添加 Key
属性并在您的 IASKSettingsDelegate
中实现以下方法,来定义 PSGroupSpecifier
段的自定义标题视图:
- (UIView *)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView *)tableView viewForHeaderForSection:(NSInteger)section;
您可以通过实现以下方法来调整标题的高度:
- (CGFloat)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView*)tableView heightForHeaderForSection:(NSInteger)section;
对于更简单的标题自定义,而无需自定义视图,并且如果尚未实现 -settingsViewController:tableView:viewForHeaderForSection:
方法或该方法返回部分的 nil
,请实现以下方法:
- (NSString *)settingsViewController:(id<IASKViewController>)settingsViewController tableView:(UITableView*)tableView titleForHeaderForSection:(NSInteger)section;
如果该方法返回 nil
或长度为 0 的字符串,将使用 .plist
中定义的标题。
此行为类似于自定义表视图单元格。当实现方法时,如果需要,可以通过索引方便地从其索引检索部分键:
NSString *key = [settingsViewController.settingsReader keyForSection:section];
查看演示应用程序以获取具体示例。
对于页脚自定义,可以类似地实现 IASKSettingsDelegate
协议中的三个方法。
对于子窗格元素 (PSChildPaneSpecifier
),Apple 需要一个 file
键,用于指定子 plist。InAppSettingsKit 允许选择性地指定 IASKViewControllerClass
和 IASKViewControllerSelector
。在这种情况下,通过实例化指定类的 UIViewController 子类并使用 IASKViewControllerSelector
中指定的 init 方法对其进行初始化来显示子窗格。选择器必须有两个参数:一个是 Settings bundle 中的文件名的 NSString
参数,另一个是 IASKSpecifier
。然后,将自定义视图控制器推送到导航堆栈上。有关更多详细信息,请参见示例应用程序。
或者,指定 IASKViewControllerStoryBoardId
以从主storyboard启动一个viewcontroller。指定 IASKViewControllerStoryBoardFile
以使用应用 Info.plist
中主 storyboard 以外的 storyboard。
作为子窗格元素 (PSChildPaneSpecifier
) 的 IASKViewControllerClass
和 IASKViewControllerSelector
的替代方法,InAppSettingsKit 能够通过执行在您的 storyboard 中定义的任何 segue 来导航到另一个视图控制器。为此,请在 IASKSegueIdentifier
中指定 segue 标识符。
IASKSubtitle
键允许为以下元素定义副标题:Toggle、ChildPane、OpenURL、MailCompose、Button。使用副标题意味着左对齐。如果可用且未指定 IASKSubtitle
,则子窗格将其值显示为副标题。副标题可以是可本地化的字符串或具有可本地化的副标题的字典,具体取决于当前值。YES
和 NO
用作布尔切换值的键。该字典可能包含一个 __default__
键,用于在没有匹配的键时定义副标题。
对于某些元素类型,可以添加具有以下值的 IASKTextAlignment
属性以覆盖默认对齐方式:
IASKUITextAlignmentLeft
(ChildPane, TextField, Buttons, OpenURL, MailCompose)IASKUITextAlignmentCenter
(ChildPane, Buttons, OpenURL)IASKUITextAlignmentRight
(ChildPane, TextField, Buttons, OpenURL, MailCompose)默认情况下,设置表中的标签以可变的字体大小显示,尤其方便挤入长本地化(注意:如果标签太长,这可能会破坏 Settings.app 中的外观!)。要禁用此行为,请添加一个值为 NO
的 IASKAdjustsFontSizeToFitWidth
布尔属性。
所有元素类型(滑块已经具有 MinimumValueImage
除外)都支持单元格左侧的图标图像。您可以在可选的 IASKCellImage
属性中指定图像名称。自动附加“.png”或“@2x.png”后缀,并在项目中搜索。可选地,您可以在项目中添加带有后缀“Highlighted.png”或“Highlighted@2x.png”的图像,当选择单元格时(对于 Buttons 和 ChildPanes),它将自动用作高亮图像。如果未在项目中找到该图像作为资源,InAppSettingsKit 将回退到 SF Symbols。
IASKPlaceholder
键允许为 TextField 和 TextView (IASKTextViewSpecifier
) 定义占位符。
为了支持基于内容类型的自动填充,请添加 IASKTextContentType
键,该键接受 UITextContentType 的(无前缀)常量名称。示例:要使用 UITextContentTypeEmailAddress
配置文本字段,请使用 IASKTextContentType
: EmailAddress
。
可以使用代理回调来验证文本字段:
- (IASKValidationResult)settingsViewController:(IASKAppSettingsViewController*)settingsViewController validateSpecifier:(IASKSpecifier*)specifier textField:(IASKTextField*)textField previousValue:(nullable NSString*)previousValue replacement:(NSString* _Nonnull __autoreleasing *_Nullable)replacement;
回调函数接收 IASKTextField
,它是 UITextField
的子类,允许在出现验证错误时(例如,红色文本)设置文本字段的样式。 它包含一个 replacement out 参数来替换无效文本。 返回 IASKValidationResultFailedWithShake
会让文本字段抖动,以直观地指示验证错误。
PSToggleSwitchSpecifier
开关默认使用 UISwitch
。 通过指定选项 IASKToggleStyle
: Checkmark
,选定的键会显示复选标记。
多值列表 (PSMultiValueSpecifier
) 和单选组 (PSRadioGroupSpecifier
) 可以从代理动态获取它们的值和标题,而不是从静态的 Plist 文件中获取。 在您的 IASKSettingsDelegate
中实现以下两个方法。
- (NSArray*)settingsViewController:(IASKAppSettingsViewController*)sender valuesForSpecifier:(IASKSpecifier*)specifier;
- (NSArray<NSString*>*)settingsViewController:(IASKAppSettingsViewController*)sender titlesForSpecifier:(IASKSpecifier*)specifier;
示例应用程序返回所有国家/地区代码的列表作为值,以及本地化的国家/地区名称作为标题。
可以通过在 Plist 中添加 true
的 Boolean 值 DisplaySortedByTitle
键,按字母顺序对多值列表进行排序。 可以为多值列表条目提供图像。 通过 IconNames
属性(在 Values/Titles/ShortTitles 等旁边)指定图像。 多值列表支持 IASKQuickMultiValueSelection
boolean 键。 如果设置为 true,子视图控制器将在选择后弹出,因此无需点击返回按钮。
IASK 的默认行为是将设置存储在 [NSUserDefaults standardUserDefaults]
中。 但是,可以通过在 IASKAppSettingsViewController
上设置 settingsStore
属性来更改此行为。 IASK 带有两个存储实现:IASKSettingsStoreUserDefaults
(默认实现)和 IASKSettingsStoreFile
,它们在您选择路径的文件中读取和写入设置。 如果您需要更具体的内容,您也可以选择创建自己的存储。 创建自己的存储的最简单方法是创建 IASKAbstractSettingsStore
的子类。 只需要重写 3 个方法。 有关更多详细信息,请参见 IASKSettingsStore.{h,m}
。
对于每个更改的设置键,都会发送 IASKSettingChangedNotification
通知。 该通知的 object
是发送视图控制器,userInfo
字典包含受影响键的键和新值。
有时,选项之间相互依赖。 例如,您可能想要有一个“自动连接”开关,并允许用户在启用时设置用户名和密码。 要对特定设置的更改做出反应,请使用上面解释的 IASKSettingChangedNotification
通知。
要隐藏一组单元格,请使用
- (void)[IASKAppSettingsViewController setHiddenKeys:(NSSet*)hiddenKeys animated:(BOOL)animated];
或者使用非动画版本
@property (nonatomic, strong) NSSet *hiddenKeys;
有关更多详细信息,请参见示例应用程序。 在 hiddenKeys
中包含 PSGroupSpecifier
键会隐藏整个节。
设置属性列表支持 DefaultValue
参数,以便在 NSUserDefaults
中未存储任何值的情况下显示默认值。 但是,当应用程序向 NSUserDefaults
查询该值时,该默认值不会被传播。 这是有道理的,因为 NSUserDefaults
不知道设置属性列表。
要为各种设置键初始设置值,NSUserDefaults
提供了 registerDefaults:
方法,该方法采用一个“回退”值字典,如果未存储任何值,则从 NSUserDefaults
返回这些值。 这通常在应用程序启动时调用。
但是,创建和维护该字典可能很麻烦,并且存在该字典和设置默认值不同步的风险。
为了解决这个问题,IASKSettingsReader
提供了一种方法,通过遍历 Root.plist 和所有子 Plist 并收集所有键的 DefaultValue
来生成该字典。
NSDictionary *defaultDict = [appSettingsViewController.settingsReader gatherDefaultsLimitedToEditableFields:YES];
[NSUserDefaults.standardUserDefaults registerDefaults:defaultDict];
要将您的 NSUserDefaults
与 iCloud 同步,还有一个名为 FTiCloudSync 的项目,该项目被实现为 NSUserDefaults
上的一个类别:所有写入和删除请求都会自动转发到 iCloud,并且来自 iCloud 的所有更新都会自动存储在 NSUserDefaults
中。 如果使用基于标准 NSUserDefaults
的存储,InAppSettingsKit 会自动更新 UI。
请不要使用 Github issues 提交支持请求,我们会关闭它们。 请在 StackOverflow 上使用标签 inappsettingskit
发布您的疑问。
我们根据宽松的 BSD 许可证发布了代码,以便可以将其包含在每个项目中,无论是免费的还是付费的应用程序。 我们唯一的要求是给原始开发人员一些 credit。 包含 credits 的最简单方法是在代码中保留“Powered by InAppSettingsKit”声明。 如果您决定删除此声明,那么在 App Store 描述页面或主页上明显提及也是可以的。
最初由 Luc Vandal 开发,Ortwin Gentz (Mastodon) 接管了开发并继续更新该框架。 InAppSettingsKit 在 FutureTap 的 Where To? 应用程序中使用,因此我们身体力行!
如果您想支持我的开源工作,请考虑作为 赞助商 加入我! 💪️ 您的赞助使我能够花更多的时间在 InAppSettingsKit 和其他社区项目上。 谢谢!