Realm Studio
Realm 开发工具使用 Realm Studio,让开发更有效率。
您可以轻松地打开任何 Realm 数据库文件或者 Realm 对象服务器部署对象,并对其进行分析。Realm Studio 允许您轻松地打开并编辑本地 Realm 数据库和可同步 Realm 数据库,此外还可以管理 Realm 平台。可运行在 Mac、Windows 以及 Linux 平台。
项目中添加RealmSwift
Carthage
- Cartfile添加
github "realm/realm-cocoa"
; - 终端中cd到工程文件;
- 运行
carthage update --platform iOS
- 在
Carthage/Build/iOS/
中将Realm.framework
、RealmSwift.framework
添加到Xcode 工程的 “General” 设置选项卡的 “Linked Frameworks and Libraries” 部分内; - 路径配置如下图。Carthage传送门
CocoaPods
- 执行 pod repo update,从而让 CocoaPods 更新至目前最新可用的 Realm 版本;
- 在您的 Podfile 中,将 use_frameworks! 和 pod 'RealmSwift' 添加到主应用目标和测试目标中;
- 在命令行中执行 pod install;
- 使用由 CocoaPods 生成的 .xcworkspace 文件来编写工程。
动态库
前往 Xcode 工程的 “General” 设置选项卡中,在 ios/、osx/、tvos/ 或者 watchos/ 目录中选择适合您项目的 Swift 版本目录,将 Realm.framework 和 RealmSwift.framework 拖曳到 “Embedded Binaries” 部分内。请确保勾选了 Copy items if needed(除非项目中有多个平台都需要使用 Realm ),然后单击 Finish 按钮;
在单元测试目标的 “Build Settings” 中,将 RealmSwift.framework 的父目录添加到 “Framework Search Paths” 部分中;
如果在 iOS、watchOS 或者 tvOS 工程中使用 Realm,请在应用目标的 “Build Phases” 中创建一条新的 “Run Script Phase”,然后将下面这段代码粘贴到脚本文本框内:
Copy to clipboardbash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework/strip-frameworks.sh"
因为要绕过App Store 出现的提交 bug,因此这一步在打包通用二进制文件时是必须的。
使用RealmSwift
数据模型
1.创建数据模型
在使用Realm中存储的数据模型都要是Object
类的子类。
import Foundation
import RealmSwift
class Dog: Object {
@objc dynamic var id: Int = 0
@objc dynamic var name: String?
}
class Cat: Object {
@objc dynamic var name: String?
}
@objc
是为了兼容Swift4
2.支持的属性类型
Realm支持的类型:Bool、Int、Int8、Int16、Int32、Int64、Double、Float、String、Date 以及 Data。
其中String、Date 以及 Data 属性都是可空的,Object 属性必须可空。
在上面Dog
中你会发现数值类型是不能写成Int?
类型的,在Realm中可选数值类型要如下设置:
// 可选 int 属性,默认为 nil
// RealmOption 属性应该始终用 `let` 进行声明,
// 因为直接对其进行赋值并不会起任何作用
let age = RealmOptional<Int>()
let realm = try! Realm()
try! realm.write() {
var person = realm.create(Person.self, value: ["Jane", 27])
// // 读取或者修改 `RealmOptional` 可以通过 `value` 属性实现
person.age.value = 28
}
RealmOptional 支持 Int、Float、Double、Bool,以及所有大小的 Int 版本(包括 Int8、Int16、Int32、Int64)。
3.设置主键
override static func primaryKey() -> String? {
return "id"
}
实现这个方法就能直接设置主键
4.索引属性
override static func indexedProperties() -> [String] {
return ["title"]
}
实现indexedProperties
进行设置索引属性。
Realm 支持为字符串、整型、布尔值以及 Date 属性建立索引。
5.属性备忘单
类型 | 非可空值形式 | 可空值形式 |
---|---|---|
Bool | @objc dynamic var value = false | let value = RealmOptional<Bool>() |
Int | @objc dynamic var value = 0 | let value = RealmOptional<Int>() |
Float | @objc dynamic var value: Float = 0.0 | let value = RealmOptional<Float>() |
Double | @objc dynamic var value: Double = 0. | let value = RealmOptional<Double>() |
String | @objc dynamic var value = "" | @objc dynamic var value: String? = nil |
Data | @objc dynamic var value = Data() | @objc dynamic var value: Data? = nil |
Date | @objc dynamic var value = Date() | @objc dynamic var value: Date? = ni |
Object | 不存在:必须是可空值 | @objc dynamic var value: Class? |
List | let value = List<Type>() | 不存在:必须是非可空值 |
LinkingObjects | let value = LinkingObjects(fromType: Class.self, property: "property") | 不存在:必须是非可空值 |
创建Realm
//MARK: - 创建数据库
/// 创建数据库
///
/// - Parameters:
/// - dataBaseName: 数据库名字
/// - isReadOnly: 是否是只读
/// - Returns: Realm实例
@discardableResult
public func creatDB(_ dataBaseName: String, isReadOnly: Bool = false) -> Realm? {
let realm = openDB(dataBaseName, isReadOnly: isReadOnly)
return realm
}
/// 打开数据库
///
/// - Parameter name: 数据库名字
/// - isReadOnly: 是否是只读
/// - Returns: Realm实例
@discardableResult
private func openDB(_ dataBaseName: String, isReadOnly: Bool = false) -> Realm? {
guard let dbPath = getCreatDatabasePath(dataBaseName) else {
return nil
}
var config = Realm.Configuration()
config.fileURL = dbPath
config.readOnly = isReadOnly
Realm.Configuration.defaultConfiguration = config
do {
let realm = try Realm.init(configuration: config)
return realm
}catch let error {
mPrint("打开或者创建数据库失败:\n\(error.localizedDescription)")
return nil
}
}
在本地生成realm.realm
文件时还会有:
-
realm.lock
- 资源锁定文件; -
realm.management
- 存放进程锁文件的目录; -
realm.note
- 用于通知的命名管道。
这些文件只是Realm维护的文件删除或者怎么着都不会出现什么问题。
在报告 Realm 问题的时候,请将这些辅助文件 (auxiliary Realm) 连同主要的 .realm 文件一同提交,因为它们很可能会包含某些对调试问题有用的信息。
打开创建的文件利用RealmStudioda打开会发现,在工程中所有继承Object
的类直接在Realm中创建了表(暂时未找到具体的原因)。
打开工程中的Realm文件
/// 打开预植的数据库
///
/// - Parameters:
/// - dataBaseName: 数据库名字
/// - isReadOnly: 是否是只读
/// - Returns: Realm实例
@discardableResult
public func openReferenceDB(_ dataBaseName: String, isReadOnly: Bool = true) -> Realm? {
guard let dbPath = getReferenceDatabasePaeh(dataBaseName) else {
return nil
}
var config = Realm.Configuration()
config.fileURL = dbPath
config.readOnly = isReadOnly
Realm.Configuration.defaultConfiguration = config
do {
let realm = try Realm.init(configuration: config)
return realm
}catch let error {
mPrint("打开或者创建数据库失败:\n\(error.localizedDescription)")
return nil
}
}
设置Realm的defaultConfiguration
/// 设置通过Realm()获取数据库的配置
///
/// - Parameters:
/// - realmName: 数据库的名字
/// - isReadOnly: 是否是这是只读
public func setDefaltRealmConfiguration(_ realmName: String,isReference: Bool = false, isReadOnly: Bool = false) -> Bool{
var realmPath: URL?
if isReference {
realmPath = getReferenceDatabasePaeh(realmName)
}else {
realmPath = getCreatDatabasePath(realmName)
}
if realmPath == nil {
return false
}
var config = Realm.Configuration()
config.fileURL = realmPath
config.readOnly = isReadOnly
Realm.Configuration.defaultConfiguration = config
return true
}
获取当前默认的数据库
/// 获取当前默认的数据
///
/// - Returns: 返回默认的Realm的数据库实例
@discardableResult
public func getDefaultRealm() -> Realm? {
do {
return try Realm()
}catch let error {
mPrint("获取默认的Realm的数据库失败:\n\(error.localizedDescription)")
return nil
}
}
如果没有配置defaultConfiguration
则会获取默认的数据库。
/Library/Developer/CoreSimulator/Devices
/26B4D5CC-1EF4-4897-8F02-BCFBE06F7C40/data
/Containers/Data/Application/7CDCBAF4-A7A2-45E4-9B8A-725E873975AD/Documents/default.realm
配置后会获取到设置路径的数据库。
/Library/Developer/CoreSimulator/Devices
/26B4D5CC-1EF4-4897-8F02-BCFBE06F7C40/data
/Containers/Data/Application/E050DEE4-71FB-4866-A10C-CBADA288D35C/Library/Caches/DB/2237DB/2237DB.realm
Realm
的实例不用全局数据共享,在配置默认数据库后你无论在什么地方获取的Realm()
都是同一个数据库。
增
//MARK: - 增
/// 创建表 || 更新表
///
/// - Parameters:
/// - type: 表向对应的对象
/// - value: 值
/// - update: 是否是更新, 如果是"true", Realm会查找对象并更新它, 否则添加对象
/// - result: 最后添加对象是成功, 如果成功将对象返回
public func creatObject(_ type: RealmSwift.Object.Type, value: Any? = nil, update: Bool = false, result: ((RealmSwift.Object?, Error?) -> Void)? = nil){
let realm = getDefaultRealm()
do {
try realm?.write {
let object = (value == nil) ? realm?.create(type) : realm?.create(type, value: value!, update: update)
result?(object, nil)
}
} catch let error {
mPrint("获取默认的Realm的数据库失败:\n\(error.localizedDescription)")
result?(nil, error)
}
}
/// 添加数据 || 根据主键更新数据
///
/// - Parameters:
/// - object: 要添加的数据
/// - update: 是否更新, 如果是true
/// - result: 添加数据的状态
public func addObject(_ object: RealmSwift.Object, update: Bool = false, result: ((Error?) -> Void)? = nil) {
let realm = getDefaultRealm()
do {
try realm?.write {
realm?.add(object, update: update)
result?(nil)
}
} catch let error {
mPrint("添加数据失败:\n \(error.localizedDescription)")
result?(error)
}
}
删
//MARK: - 删
/// 删除数据
///
/// - Parameters:
/// - object: 要删除的对象
/// - result: 删除的状态
public func deleteObject(_ object: RealmSwift.Object, result: ((Error?) -> Void)? = nil) {
let realm = getDefaultRealm()
do {
try realm?.write {
realm?.delete(object)
result?(nil)
}
} catch let error {
mPrint("添加数据失败:\n \(error.localizedDescription)")
result?(error)
}
}
/// 删除当前数据库中所有的数据
///
/// - Parameter result: 删除的状态
public func deleteAllObject(result: ((Error?) -> Void)? = nil) {
let realm = getDefaultRealm()
do {
try realm?.write {
realm?.deleteAll()
result?(nil)
}
} catch let error {
mPrint("添加数据失败:\n \(error.localizedDescription)")
result?(error)
}
}
/// 删除当前打开的数据库
///
/// - Parameter dataBaseName: 数据库的名字
/// - Returns: 删除的状态
@discardableResult
public func deleteCreatDBFile() -> Bool {
return autoreleasepool { () -> Bool in
let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
let realmURLs = [
realmURL,
realmURL.appendingPathExtension("lock"),
realmURL.appendingPathExtension("note"),
realmURL.appendingPathExtension("management")
]
for URL in realmURLs {
do {
try FileManager.default.removeItem(at: URL)
return true
} catch {
// 错误处理
return false
}
}
return false
}
}
改
//MARK: - 改
/// 根据主键进行更新
///
/// - Parameters:
/// - object: 要更新的对象
/// - update: 是否根据主键更新, 如果是"false"则是添加数据
/// - result: 更新数据的结果
public func updateObject(_ object: RealmSwift.Object, update: Bool = true, result: ((Error?) -> Void)? = nil) {
addObject(object, update: update, result: result)
}
/// 根据主键进行更新
///
/// - Parameters:
/// - type: 要更新的对象类型
/// - value: 要更新的值, 例如: ["id": 1, "price": 9000.0]
/// - update: 是否根据主键进行更新, 如果为"false"则为创建表
/// - result: 更新的结果
public func updateObject(_ type: RealmSwift.Object.Type, value: Any? = nil, update: Bool = true, result: ((RealmSwift.Object?, Error?) -> Void)? = nil) {
creatObject(type, value: value, update: update, result: result)
}
/// 直接更新对象
///
/// - Parameters:
/// - property: 要更改的属性
/// - value: 更改的值
/// - Returns: 更改的结果
@discardableResult
public func updateObject( property: inout Any, value: Any) -> Bool {
let realm = getDefaultRealm()
do {
try realm?.write {
property = value
}
return true
} catch let error {
mPrint("直接更新对象属性错误: \(error.localizedDescription)")
return false
}
}
/// 更改表中所有的字段的值
///
/// - Parameters:
/// - type: 表的对象类型
/// - key: 要更改的字段名
/// - value: 更改的值
/// - Returns: 返回更改结果
public func updateObjects(type: RealmSwift.Object.Type, key: String, value: Any) -> Bool {
let objects = getObjects(type: type)
do {
try getDefaultRealm()?.write {
objects?.setValue(value, forKeyPath: key)
}
return true
} catch let error {
mPrint("更改一个表中的所有数据错误: \(error.localizedDescription)")
return false
}
}
/// 根据主键进行对某个对象中的数据进行更新
///
/// - Parameters:
/// - type: 表类型
/// - primaryKey: 主键
/// - key: 要更改属性
/// - value: 更改的值
/// - Returns: 更改的状态
public func updateObject(type: RealmSwift.Object.Type, primaryKey: Any, key: String, value: Any) -> Bool {
let object = getObjectWithPrimaryKey(type: type, primaryKey: primaryKey)
do {
try getDefaultRealm()?.write {
object?.setValue(value, forKeyPath: key)
}
return true
} catch let error {
mPrint("更新数据出错: \(error.localizedDescription)")
return false
}
}
查
//MARK: - 查
/// 查找一个表中的所有的数据
///
/// - Parameter type: 对象类型
/// - Returns: 查到的数据
public func getObjects(type: RealmSwift.Object.Type) -> RealmSwift.Results<RealmSwift.Object>?{
return getDefaultRealm()?.objects(type)
}
/// 根据主键查找某个对象
///
/// - Parameters:
/// - type: 对象类型
/// - primaryKey: 主键
/// - Returns: 查到的数据
public func getObjectWithPrimaryKey(type: RealmSwift.Object.Type, primaryKey: Any) -> RealmSwift.Object? {
return getDefaultRealm()?.object(ofType: type, forPrimaryKey: primaryKey)
}
/// 使用断言字符串查询
///
/// - Parameters:
/// - type: 对象类型
/// - filter: 过滤条件
/// - Returns: 查询到的数据
/// - example:
/// - var tanDogs = realm.objects(Dog.self).filter("color = 'tan' AND name BEGINSWITH 'B'")
public func getObject(type: RealmSwift.Object.Type, filter: String) -> RealmSwift.Results<RealmSwift.Object>? {
return getObjects(type: type)?.filter(filter)
}
/// 使用谓词进行查询
///
/// - Parameters:
/// - type: 对象类型
/// - predicate: 谓词对象
/// - Returns: 查询到的数据
/// - example:
/// - let predicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "tan", "B")
/// - tanDogs = realm.objects(Dog.self).filter(predicate)
public func getObject(type: RealmSwift.Object.Type, predicate: NSPredicate) -> RealmSwift.Results<RealmSwift.Object>? {
return getObjects(type: type)?.filter(predicate)
}
/// 对查询的数据进行排序,请注意, 不支持 将多个属性用作排序基准,此外也无法链式排序(只有最后一个 sorted 调用会被使用)。 如果要对多个属性进行排序,请使用 sorted(by:) 方法,然后向其中输入多个 SortDescriptor 对象。
///
/// - Parameters:
/// - type: 对象类型
/// - filter: 过滤条件
/// - sortedKey: 需要排序的字段
/// - Returns: 最后的结果
public func getObject(type: RealmSwift.Object.Type, filter: String, sortedKey: String) -> RealmSwift.Results<RealmSwift.Object>? {
return getObject(type: type, filter: filter)?.sorted(byKeyPath: sortedKey)
}
/// 对查询的数据进行排序, 请注意, 不支持 将多个属性用作排序基准,此外也无法链式排序(只有最后一个 sorted 调用会被使用)。 如果要对多个属性进行排序,请使用 sorted(by:) 方法,然后向其中输入多个 SortDescriptor 对象。
///
/// - Parameters:
/// - type: 队形类型
/// - predicate: 谓词对象
/// - sortedKey: 排序的字段
/// - Returns: 排序后的数据
public func getObject(type: RealmSwift.Object.Type, predicate: NSPredicate, sortedKey: String) -> RealmSwift.Results<RealmSwift.Object>? {
return getObject(type: type, predicate: predicate)?.sorted(byKeyPath: sortedKey)
}
集合
Realm 拥有许多能够表示一组对象的类型,称之为 “Realm 集合”:
- Results 类,表示queries所返回的对象集合。
- List 类,表示模型之间的对多关系。
- LinkingObjects 类,表示模型之间的双向关系。
- RealmCollection 协议,定义了所有 Realm 集合的常用接口。
- AnyRealmCollection 类,这是一个无类型的类,可以将调用转发给具体的 Realm 集合,例如 Results、List 或者 LinkingObjects。
Realm 集合类型均实现了 RealmCollection 协议,这确保 它们的行为均保持一致。这个协议继承自 CollectionType,因此它的使用方式 与标准库内的集合相同。这个协议也同样声明了其他常用的 Realm 集合 API, 比如说检索、排序、聚合操作等等。List 还存在一些额外的修改操作, 这些操作没有在协议接口中定义,比如说添加或者删除对象。
使用 RealmCollection 协议, 您可以编写能够对任意 Realm 集合进行操作的泛型代码:
Copy to clipboardfunc operateOn<C: RealmCollection>(collection: C) {
// collection 既可以是 RLMResults,也可以是 RLMArray
print("operating on collection containing \(collection.count) objects")
}
由于 Swift 类型系统的限制,必须使用诸如 AnyRealmCollection 之类的无类型封装器,才能将这个集合存储在属性或者变量中:
Copy to clipboardclass ViewController {
// let collection: RealmCollection
// ^
// error: protocol 'RealmCollection' can only be used
// as a generic constraint because it has Self or
// associated type requirements
//
// init<C: RealmCollection>(collection: C) where C.ElementType == MyModel {
// self.collection = collection
// }
let collection: AnyRealmCollection<MyModel>
init<C: RealmCollection>(collection: C) where C.ElementType == MyModel {
self.collection = AnyRealmCollection(collection)
}
}
Realm的基础使用先写到这里,更详细的可以直接看文档(真的很详细)。
在使用RealmSwift增删改查又用RxSwift封装了一层。
项目地址
参考资料
谢谢
网友评论