Flint 是一个框架,使用 Swift 的功能构建适用于 Apple 平台的 App。
根据运行时约束(系统权限、OS 版本或内购),您的应用程序的功能会被启用。Flint 会监听您的动作执行,提供增强的日志记录、自动分析事件、NSUserActivity 集成以支持 Handoff、搜索和 Siri 预测、URL 处理、Siri Shortcuts 支持、用户活动时间线用于调试等等。
它使用类似网络应用程序开发框架(如 Rails)的编码规范。但是,它利用 Swift 的静态编译和关联类型功能来提供增强的安全性和编码补全。
结果是制作出的应用程序更健壮、更精致,更少样板代码,更好的解耦,而且不需要进行巨大的架构变革或采用特定的 UI/模型方法。
我们创建 Flint,是因为我们想让人们能够轻松地为 Apple 平台构建优秀的应用程序,并充分利用本机平台的性能。
基础知识
功能(Features) 是您应用程序可以做的事情。Flint 中的功能符合 Feature
协议
import FlintCore
class DocumentManagementFeature: Feature {
static let description = "Create, Open and Save documents"
static let openDocument = action(DocumentOpenAction.self)
static func prepare(actions: FeatureActionsBuilder) {
actions.declare(openDocument)
}
}
通常有一个或多个操作,并且一些功能有子功能。
操作(Actions) 是用户可以与您的功能交互的方式,如“打开文档”、“关闭文档”、“分享文档”。操作是符合 Action
类型并声明在功能上(如上述示例),接受您定义的 input
和 presenter
类型。
import FlintCore
final class DocumentOpenAction: Action {
typealias InputType = DocumentRef
typealias PresenterType = DocumentPresenter
static var description = "Open a document"
static func perform(context: ActionContext<DocumentRef>,
presenter: DocumentPresenter,
completion: Completion) -> Completion.Status {
presenter.openDocument(context.input)
return completion.completedSync(.success)
}
}
一旦定义了操作,Flint 就可以监听应用程序执行这些操作。这解锁了许多功能,如自动 NSUserActivity
和 Siri 集成、分析跟踪和改进的调试日志。
然而,由于Flint还知道如何针对特定输入调用您的动作,它也可以为您处理所有不同的应用程序入口点,包括应用程序或深度链接URL以及包括Handoff、Spotlight和Siri建议在内的连续活动。 更多详情请参阅功能与动作指南。
关于需要内购或特定系统权限的功能怎么办?条件功能支持约束。这可以包括平台、OS版本、系统权限、内购等。由于Swift,除非您还处理了该功能当前不可用的案例,否则您的代码不能执行条件功能的操作。
import FlintCore
let premiumSubscription = AutoRenewingSubscriptionProduct(name: "💎 Premium Subscription",
description: "Unlock the Selfietron!",
productID: "SUB0001")
public class SelfieFeature: ConditionalFeature {
public static var description: String = "Selfie Posting"
public static func constraints(requirements: FeatureConstraintsBuilder) {
// Allow the user to turn this on/off themselves
requirements.userToggled(defaultValue: true)
// Require isEnabled to return `true` at runtime
requirements.runtimeEnabled()
// Require a purchase for this feature to be enabled
requirements.purchase(premiumSubscription)
// Require these permissions before the feature's actions can be used
requirements.permissions(.camera,
.photos,
.location(usage: .whenInUse))
}
...
}
需要多个权限或多种购买选项的功能可以轻松容纳,并且Flint将帮助您构建一流的权限引入UI,以最大化可以使用您功能用户数。
当您需要从条件功能执行操作时,您被迫首先检查该功能是否可用,并处理不可用的情况。
if let request = DocumentSharingFeature.share.request() {
request.perform(withInput: document, presenter: presenter)
} else {
showPremiumUpgradeOrPermissionAuthorisations()
}
这使得您的代码更清晰、更安全。团队中的每个人都可以看到哪些代码是内部特性标志的或需要购买,以及您的应用需要哪些权限。
更多详情请参阅条件功能编程指南。
处理URL
要处理传入的URL,您只需定义一个动作——一个符合Action
协议的类型,并将其添加到一个对应一个或多个URL路由的动作Feature
。
考虑一个常见的案例,比如处理通过电子邮件发送的用户注册确认链接。该URL将包含一个令牌,当用户点击时,应用程序应该打开,验证令牌,然后显示“您已登录!”屏幕。
import FlintCore
class UserAccountManagementFeature: Feature, URLMapped {
static let description = "User sign-up, sign in and sign out"
static let confirmAccount = action(ConfirmAccountAction.self)
static func prepare(actions: FeatureActionsBuilder) {
actions.declare(confirmAccount)
}
// 💥 Use `routes` to define the URLs and actions
static func urlMappings(routes: URLMappingsBuilder) {
routes.send("account/confirm", to: confirmAccount)
}
}
一旦您将自定义URL方案添加到您的Info.plist
并/或将相关域添加到您的权限中,当请求打开类似以下链接时,您的应用程序就会调用“确认账户”操作。
your-app://account/confirm
https://yourappdomain.com/account/confirm
每个动作支持多个映射,多个URL方案和多个关联域,因此不会对旧链接造成问题。您需要添加一些额外的代码到您的应用代理中,并设置UI以处理传入的动作。
由于篇幅限制,这里没有展示ConfirmAccountAction
动作类型。请参阅功能与动作指南以获取详细信息。
当然,如果需要,您也可以从代码中轻松执行应用程序中的相同动作。
UserAccountManagementFeature.confirmAccount.perform(withInput: confirmationToken, presenter: presenter)
如果您需要,可以使用Flint.linkCreator
创建链接到这些映射动作的URL。
更多细节请参阅路径编程指南。
自动切换和Siri建议支持
苹果的NSUserActivity
被广泛用于告知系统用户当前在做什么,以集成设备间的切换、Siri应用建议、一些Spotlight搜索集成以及深度链接。由于执行用户在应用中选择活动时的任意操作存在挑战,许多人往往不实施这一点。
Flint可以自动为您完成,如果您的动作也支持URL路由,则无需任何努力。
import FlintCore
final class DocumentOpenAction: Action {
typealias InputType = DocumentRef
typealias PresenterType = DocumentPresenter
static var description = "Open a document"
// 💥 Just tell Flint what activity types to use
static var activityEligibility: Set<ActivityEligibility> = [.perform, .handoff]
static func perform(context: ActionContext<DocumentRef>,
presenter: DocumentPresenter,
completion: Complettion) -> Completion.Status {
// … do the work
}
}
除了将NSUserActivityTypes
添加到您的Info.plist
并列出Flint自动生成的活动ID之外,您只需做以下几点。
当然,如果您想自定义NSUserActivity
的属性,可以通过定义prepare(activity:for:)
函数来实现。请参阅活动指南。
有关详细编程指南,请参阅活动编程指南。
当用户做什么时跟踪分析事件
大多数应用最终都需要执行某种类型的分析报告,以了解您的用户实际上在做什么。分析事件通常是一个事件ID和一组键值的字典。Flint使通过任何分析服务轻松、一致地发出这些事件变得简单,甚至可以用于您自己的自定义后端。
因此,当您的市场营销人员说他们希望他们的分析报告系统显示人们何时打开文档时,您只需在动作上设置analyticsID
属性,然后每次执行该动作时,Flint的AnalyticsReporting
组件都会自动将其收集并传递给您的分析提供商。
import FlintCore
final class DocumentOpenAction: Action {
typealias InputType = DocumentRef
typealias PresenterType = DocumentPresenter
static let description = "Open a document"
// 💥 Enable analytics with just one property!
static let analyticsID = "user-open-document"
static func perform(context: ActionContext<DocumentRef>,
presenter: DocumentPresenter,
completion: Completion) -> Completion.Status {
// … do the work
}
}
当然,您可以通过定义analyticsAttributes()
函数来自定义传递给分析提供商的数据字典。
有关详细编程指南,请参阅分析编程指南。
开始使用
Flint支持Carthage和Cocoapods。请参阅入门指南。
了解更多
这些都是冰山一角。Flint 有更多要提供给您的,通过在几乎每个地方使用协议,有很多扩展和定制点,这样您就不会被锁定在像特定分析提供商这样的任何一件事上。
如果您想查看使用 Flint 的示例项目,请访问在 Github 上的 FlintDemo-iOS 项目。您可以通过浏览它来了解一个真实应用程序可能如何使用 Flint。
理念
我们全都是 Swift 粉丝,但我们不希望成为几个星期后看不懂自己代码的聪明人。我们使用了一些令人印象深刻的 Swift 功能,让伟大的事情成为可能:面向协议编程、一些泛型和非常少量的关联类型。
我们故意避免更隐晦的模式,因为我们希望这个框架非常易于访问和便于所有人理解,无论他们为他们的代码库选择了哪种范式。
社区和贡献
我们有一个社区 Slack 您可以加入以获取帮助和讨论想法。加入 flintcore.slack.com。
我们非常愿意接受您的贡献。请在 Github 上提出问题,并讨论您的问题和建议。我们期待您的想法和合并请求。
Flint 版权归 Montana Floss Co. 所有,采用 MIT 开源许可证。