Flint是一个框架,用于利用Swift的强大功能,通过特征和动作构建用于Apple平台的应用。
您的应用的特征根据运行时约束启用;系统权限、操作系统版本或在应用内购买。它接受您的动作并提供了增强的日志记录、自动分析事件、NSUserActivity集成以实现Handoff、搜索和Siri预测、URL处理、Siri快捷键支持、用户活动时间线用于调试等等。
它使用类似于Web应用程序开发框架(如Rails)的编码约定。然而,它利用Swift的静态编译和关联类型特性来提供增强的安全性和代码补全。
结果是更健壮、更精致的应用程序,具有更少的样板代码和更好的解耦,无需进行大规模的架构转变或特定的UI/模型方法。
我们创建Flint,因为我们希望人们构建出适用于Apple平台的优质应用程序,使用户能够以更少的问题充分利用本地平台功能。
基础知识
特征是您的应用程序可以执行的操作。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)
}
}
它们通常有一个或多个动作,某些特征还具有子特征。
动作是用户可以通过您的特性执行的操作,例如“打开文档”,“关闭文档”,“分享文档”。动作是符合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以及继续活动,包括传递、Spotlight和Siri建议。在功能与操作指南中了解更多。
那么,需要应用内购买或特定系统权限的功能呢?条件功能支持约束。这些可以包括平台、操作系统版本、系统权限、应用内购买等。由于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将协助您构建一流的权限注册用户界面,以便最大程度地增加能够使用您功能用户的数量。
当您需要从条件功能执行操作时,您必须首先检查该功能是否可用,并处理其不可用的案例。
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
中,并将相关域名添加到您的权利中,当应用被要求打开类似以下URL时,就会触发“确认账户”操作:
your-app://account/confirm
https://yourappdomain.com/account/confirm
每个操作支持多个映射,支持多个URL方案和多个相关域名,因此不需要处理旧URL。需要将一些小的粘合剂代码添加到您的应用代理中,以设置当操作到来时的UI。
此处没有显示操作类型ConfirmAccountAction
,为了简洁。有关完整详情,请参阅功能与操作指南。
如果需要,您可以从代码中轻松地在您的应用中执行此相同的操作。
UserAccountManagementFeature.confirmAccount.perform(withInput: confirmationToken, presenter: presenter)
如果您需要创建指向这些映射操作的URL,可以使用Flint.linkCreator
。
有关详细信息,请参阅路由编程指南。
自动切换和Siri建议支持
Apple的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之外,这便是您需要做的事情。
您当然可以通过定义一个prepare(activity:for:)
函数来自定义NSUserActivity
的属性。请参阅活动指南。
有关活动编程的详细信息,请参阅编程指南。
当用户执行操作时跟踪分析事件
大多数应用程序都需要进行某种分析报告,以了解用户实际上在做什么。分析事件通常是一个事件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的示例项目,这里有一个FlintDemo-iOS项目在GitHub上。您可以浏览该项目来了解实际应用程序可能如何使用Flint。
哲学
我们完全支持Swift,但我们不想成为几周后都看不懂自己代码的“聪明人”。我们采用了一些使实时操作成为可能的高级Swift特性:面向协议编程、一些泛型和很少量的关联类型。
我们故意避免了更隐晦的模式,因为我们想让这个框架非常易于访问,并且每个人都能根据他们为代码库选择的范式对其进行推理。
社区和贡献
我们有一个社区Slack群组,你可以加入来获取帮助和讨论想法。加入方式为flintcore.slack.com。
我们非常欢迎你的贡献。请在GitHub上提出问题,并讨论你的问题和建议。我们期待你的想法和拉取请求。
Flint版权属于Montana Floss Co.,并遵照MIT开源许可协议。