目录
PersistenceFramework
一个用面向协议编程来封装持久化逻辑的框架。这是一个示例框架,展示了如何使用
- Cocoapods
- 面向协议编程
- 依赖注入
- 单元测试
支持的数据库
- Core Data
- Realm
安装
使用 Cocoapods,在 Podfile 中添加:
所有数据库
pod 'PersistenceFramework'
仅核心数据数据库
pod 'PersistenceFramework/CoreData'
仅Realm数据库
pod 'PersistenceFramework/Realm'
如何使用
要使用此框架,您只需了解几个协议。这使得在数据库之间切换变得简单。
协议
DatabaseProtocol
这是任何数据库必须实现的基本协议。其想法是提供数据库应提供的最常用的功能。对于任何其他用途,您可以恢复数据库上下文并实现自定义代码。
public protocol DatabaseProtocol {
...
func create<ReturnType: DatabaseObjectTypeProtocol>() -> ReturnType?
func recover<ReturnType: DatabaseObjectTypeProtocol>(key: String, value: String) -> [ReturnType]?
func delete(_ object: DatabaseObjectType) -> Bool
func getContext() -> DatabaseContextType
}
可保存协议
Core Data 中使用的可选协议。
public protocol Saveable {
func save() throws
}
可更新协议
Realm 中使用的可选协议。
public protocol Updatable {
func update<DatabaseObject: DatabaseObjectTypeProtocol>(_ object: DatabaseObject) -> Bool
}
注意: 这只是一个示例。您可以免费复制此存储库并创建自己的版本。
创建者
要创建或恢复数据库,您必须使用针对所需数据库的特定 DatabaseBuilder 结构。该结构需要用每个情况的必要信息进行初始化(Core Data 需要不同的事物,不同于 Realm)
Core Data 创建者
实例化此结构以创建 Core Data 数据库。您可以根据需要拥有任意多个。只需确保使用不同的 "名称"。
您需要提供的信息是
- 数据库名称:带有数据库名称的字符串参数。
- 捆绑包:NSManagedObjectModel 文件所在的位置的捆绑包,在迁移时需要。
- 模型URL:NSManagedObjectModel 文件的路径的 URL。
public struct CoreDataBuilder: CoreDataBuilderProtocol {
public typealias Database = CoreDataManager
public let databaseName: String
public let bundle: Bundle
public let modelURL: URL
public func create() throws -> CoreDataManager {
...
}
}
使用示例
let modelURL = Bundle.main.url(forResource: "MyModel", withExtension:"momd")
let databaseBuilder = CoreDataBuilder(databaseName: "CoreDataDatabaseName", bundle: Bundle.main, modelURL: modelURL)
let database = try? databaseBuilder.create() as CoreDataManager
Realm 创建者
实例化此结构以创建 Realm 数据库。您可以根据需要拥有任意多个。只需确保使用不同的 "名称"。
您需要提供的信息是
- 数据库名称:带有数据库名称的字符串参数。
- 密码短语:用于加密数据库的字符串密钥。不能为空字符串。
- 版本号:包含当前版本号的 Uint64。默认值为 0。(可选参数)
- 迁移块:当模型更改时需要迁移块。默认值为 nil。(可选参数)
public struct RealmBuilder: RealmBuilderProtocol {
public typealias Database = RealmManager
public let databaseName: String
public let passphrase: String
public let schemaVersion: UInt64
public let migrationBlock: MigrationBlock?
public func create() throws -> RealmManager {
...
}
}
使用示例
let databaseBuilder = RealmBuilder(databaseName: "RealmDatabaseName", passphrase: "Passphrase")
let database = try? databaseBuilder.create() as RealmManager
注意: 这只是一个示例。您可以免费复制此存储库并创建自己的版本。
使用
常规
创建
创建新用户实体的示例
let newObject: User? = database.create()
恢复
恢复所有用户实体的示例
let recoveredObjects: [User]? = database.recover()
恢复特定用户实体的示例。可以用任何属性,因此返回一个数组。例如:返回所有名为“John”的用户
let recoveredObjects: [User]? = database.recover(key: "name", value: "John")
删除
删除特定用户实体的示例。如果操作成功返回true,如果失败返回false
let result = database.delete(objectToDelete)
核心数据
保存
自定义Core Data方法保存上下文
try? database.save()
Realm
更新
自定义Realm方法更新特定 用户 实体。如果操作成功返回true,否则返回false
let result = database.update(newObject)
添加自定义代码
要使用数据库的其他功能,您可以恢复上下文并使用它
let context = database.getContext()
迁移
Core Data
-
轻量级迁移:只需要像往常一样初始化数据库,框架会自动迁移。
-
重量级迁移:需要在数据库的同一体中创建一个NSMappingModel来完成。如果有需要,在NSMappingModel中也可以设置一个NSEntityMigrationPolicy类型类,并为迁移过程添加自定义逻辑。
Realm
当模型更改时,您需要遵循以下步骤
- 提高模式版本
- 在Builder结构中创建迁移块。下面有一些示例
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1 && self.lastSchemaVersion >= 1) {
print("Automatic migration")
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
if (oldSchemaVersion < 2 && self.lastSchemaVersion >= 2) {
// The enumerateObjects(ofType:_:) method iterates
// over every Person object stored in the Realm file
migration.enumerateObjects(ofType: Person.className()) { oldObject, newObject in
// combine name fields into a single field
let firstName = oldObject!["firstName"] as! String
let lastName = oldObject!["lastName"] as! String
newObject!["fullName"] = "\(firstName) + \(lastName)"
}
}
if (oldSchemaVersion < 3 && self.lastSchemaVersion >= 3) {
// The renaming operation should be done outside of calls to `enumerateObjects(ofType: _:)`.
migration.renameProperty(onType: Person.className(), from: "age", to: "yearsSinceBirth")
}
}