Flapjack
Flapjack 是一个具有两个主要目标的 iOS/macOS/tvOS 框架。
- 帮助您将模型驱动型数据库持久化层从应用程序的其他部分进行抽象
- 将数据库层的 API 简化为易于使用、易于记忆的完整 Swift API
它允许您 跳过 与数据库层(如 Core Data)相关的常见样板代码,并允许您在 更早 的时候在应用程序中引入结构化、合理的持久化数据,让您有更多的时间创建您真正想要的应用程序。我们正在 O'Reilly Media 的 iOS 应用程序中使用它,如果您喜欢所看到的内容,也许您也会。
入门指南
Swift 包管理器
Swift 包管理器是使用 Flapjack 的首选方式。将以下内容添加到您的 Package.swift
文件中的 dependencies
数组中。
.package(name: "Flapjack", url: "https://github.com/oreillymedia/flapjack.git", .upToNextMajor(from: "0.8.1"))
然后,您需要将Flapjack
指定为目标项目中希望使用的依赖项。您还可以导入FlapjackCoreData
和FlapjackUIKit
。
.package(name: "FlapjackCoreData", url: "https://github.com/oreillymedia/flapjack.git", .upToNextMajor(from: "0.8.1"))
.package(name: "FlapjackUIKit", url: "https://github.com/oreillymedia/flapjack.git", .upToNextMajor(from: "0.8.1"))
CocoaPods
Flapjack还可以通过CocoaPods获取。要安装它,只需将以下行添加到您的Podfile中。
pod 'Flapjack', '0.8.1'
# If you're using Core Data...
pod 'Flapjack/CoreData', '0.8.1'
# If you're targeting iOS and want some helpers...
pod 'Flapjack/UIKit', '0.8.1'
然后在命令行中运行pod install
。
用法
完整的文档即将推出,但以下是Flapjack所提供功能的详细介绍。
在您的iOS项目(例如在您的UIApplicationDelegate
中),使用以下代码启动(如果使用Core Data;计划支持更多数据库)。
import Flapjack
// Create the DataAccess object, your main point-of-entry for persistence.
// You can also pass in `.sql(filename: "YourCoreDataStore.sql")`.
let dataAccess = CoreDataAccess(name: "YourCoreDataStore", type: .memory)
// Then tell the stack to configure itself.
dataAccess.prepareStack(asynchronously: true) { error in
if let error = error {
print(error.localizedDescription)
}
// Make sure you retain your `dataAccess` variable, and now you're all
// ready to go!
}
为了让模型对象参加Flapjack提供的简化API,您需要确保它们符合DataObject
。以下是一个示例,如具有在Core Data模型中定义的字段identifier
、flavor
和radius
的Pancake
类。
extension Pancake: DataObject {
// The type of your primary key, if you have one of your own.
public typealias PrimaryKeyType = String
// The name of the entity as Core Data knows it.
public static var representedName: String {
return "Pancake"
}
// The key path to your model's primary key.
public static var primaryKeyPath: String {
return #keyPath(identifier)
}
// An array of sorting criteria.
public static var defaultSorters: [SortDescriptor] {
return [
SortDescriptor(#keyPath(flavor), ascending: true, caseInsensitive: true),
SortDescriptor(#keyPath(radius), ascending: false)
]
}
}
现在您可以做饭了。与数据存储器的交互甚至更简单。
// Get every pancake.
let pancakes = dataAccess.mainContext.objects(ofType: Pancake.self)
// Get just the chocolate chip ones.
let pancakes = dataAccess.mainContext.objects(ofType: Pancake.self, attributes: ["flavor": "Chocolate Chip"])
// Create your own.
let pancake = dataAccess.mainContext.create(Pancake.self, attributes: ["flavor": "Rhubarb"])
// Save your changes.
let error = context.persist()
假设您不希望在主线程上执行昂贵的数据库操作。Flapjack的Core Data支持遵循此类操作的最佳实践。
dataAccess.performInBackground { [weak self] context in
let pancake = context.create(Pancake.self, attributes: ["flavor": flavor, "radius": radius, "height": height])
let error = context.persist()
DispatchQueue.main.async {
guard let `self` = self else {
return
}
let foregroundPancake = self.dataAccess.mainContext.object(ofType: Pancake.self, objectID: pancake.objectID)
completion(foregroundPancake, error)
}
}
讨厌你的数据库了吗?也有一个函数可以做到这一点。
dataAccess.deleteDatabase(rebuild: true) { error in
if let error = error {
print(error.localizedDescription)
}
// It's almost as if it never happened.
}
数据源
如果 Flapjack 没有提供自动监听模型变化的方式,这 wouldn't be nearly as much fun. 《DataSource》和《SingleDataSource》协议分别定义了监听持久化对象集合《或》单个对象变化的方法。如果您针对Core Data,这两个协议的实现(《CoreDataSource》和《CoreSingleDataSource》)分别由《NSFetchResultsController》和《NSManagedObjectContextObjectsDidChange》提供支持。
import Flapjack
let dataSourceFactory = CoreDataSourceFactory(dataAccess: dataAccess)
let queryAttributes = ["radius": 2.0, "flavor": "Chocolate Chip"]
let dataSource: CoreDataSource<Pancake> = dataSourceFactory.vendObjectsDataSource(attributes: queryAttributes, sectionProperty: "flavor", limit: 100)
// Prepare yourself for pancakes, but only chocolate chip ones bigger than a 2" radius, and no more than 100.
// This block fires every time the data source picks up an insert/change/deletion.
dataSource.onChange = { itemChanges, sectionChanges in
// If you've added `Flapjack/UIKit` to your Podfile, you get helper extensions!
self.tableView.performBatchUpdates(itemChanges, sectionChanges: sectionChanges)
// Get a specific pancake:
print("\(String(describing: dataSource.object(at: IndexPath(item: 0, section: 0))))")
}
// Kick off a call to start listening (and immediately fire `.onChange` with all existing results).
dataSource.execute()
有关使用《CoreDataSource》的更完整示例,请参阅AutomaticViewController.swift。若要了解在不使用它的情况下访问存储数据的步骤,请参阅ManualViewController.swift。
迁移
“简化”Core Data迁移支持目前正在不断发展,以下是您目前可以期待的内容。Flapjack有一个您可以选择的《Migrator》类,您将使用该对象为您的《DataAccess》类提供迁移数据存储的方法。这个协议现在相对较简单,但如果你查看这个对象的Core Data实现(《CoreDataMigrator》),你可以看到它是如何结合在一起的。这是我们处理iOS应用程序中的迁移的非常接近的适配。以下是步骤,一步一步。
- 通过实现《DataAccessDelegate》,您将在堆栈准备好《Migrator》时收到通知。
- 作为对这个代理调用的响应,您将使用其中存储数据文件的《storeURL》和存储编译模型所在的《bundle》初始化并返回一个《CoreDataMigrator》。
- 然后,《DataAccess》对象将处理其余的内容,这本质上是一个对《migrate()`》的调用。
- 在调用《migrate()`》时,创建一个临时文件夹来存储所有中间文件。
- 然后,扫描编译后的数据模型以找到所有可用的模型版本,接着我们也尝试确定哪个版本是《当前》版本,然后我们根据不同的版本构建一个迭代的迁移版本列表(将支持提供要迁移的自定义版本列表)。
- 然后,在每个版本之间,我们根据是否有显式映射模型找到,进行重量级迁移或轻量级迁移。
作者
- Matt Blackmon (<a href="https://github.com/mblackmon">@mblackmon</a>)
- 劳拉·迪基 (@lj-dickey)
- 本·克雷格尔 (@kreeger)
- 斯科特·斯塔 (@awaltzforvenus)
许可证
Flapjack 采用 MIT 许可证。更多信息请参阅 LICENSE 文件。