在之前的swift 数据持久化之CoreData一文中,曾提到过Realm
,说会出一篇使用教程,前段时间断断续续一直在Python
的道路上探索,时隔5个月,打算在今天把落下的Realm
补上。
Realm简介
- 一个跨平台的移动终端数据库,基于自己的持久化引擎
- 比 SQLite 和 Core Data 更好更快更易用,可搭配自家
RealmBrowser
查看数据- 完全免费
注:在
Objective-C
中是使用Realm
,在Swift
中名字有了变化,叫做RealmSwift
1.集成到项目
在集成的时候可以手动集成,也可以用cocoapods
集成,因为SDK比较大,用cocoapods集成比较慢,如果嫌速度慢,可以去官网下载最新的SDK版本,然后选择对应的swift语言版本(不同版本对应SDK也不相同)手动添加到项目中,手动集成的时候需要注意,如果出现如下错误:
则需要设置工程,在General
->Embedded Binaries
中导入如下framework
cocoapods
集成时在终端输入pod search RealmSwift
可以查看相关信息
指定对应版本(如果项目多人合作,建议所有pod项目指定版本号)然后在podfile
中添加即可。
2.用法
RealmSwift支持以下的属性类型:Bool、Int8、Int16、Int32、Int64、Double、Float、String、Date、Data
,支持一对一,一对多模型存储。
2.1 数据库配置
数据库加密和存储位置,可以根据需求进行操作。但是由于开发过程中涉及到数据迁移,所以必须做迁移相关配置,否则,在模型进行更改之后,数据无法进行迁移从而导致崩溃。
下面做一个最简单的数据迁移配置
在AppDelegate.swift
中import RealmSwift
,然后在application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
中添加如下代码:
let config = Realm.Configuration(
schemaVersion: 0,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 0) {}
})
Realm.Configuration.defaultConfiguration = config
当数据模型属性改变,需要迁移数据时,只需要将schemaVersion: 0
和oldSchemaVersion < 0
中的数字+1
即可。当然设置更大也行,但是不允许比之前的版本号小。
2.2 数据模型设计
这里举一个一对多模型例子,其他的举一反三
设计一个Person
模型,import RealmSwift
,继承自Object
,让其具有name
和id
属性,并将id
设置为主键(主键的唯一性由开发者保证)
再设计一个Dog
模型,继承自Object
,让其具有name
属性,一个Person
可以拥有多个Dog
(用List
实现模型一对多)
代码如下:
import UIKit
import RealmSwift
class Person: Object {
@objc dynamic var name = ""
var dogs = List<Dog>()
@objc dynamic var id = 0
//设置主键
override class func primaryKey() -> String? {
return "id"
}
}
class Dog: Object {
@objc dynamic var name = ""
}
2.3 增
为了方便代码维护,这里新建一个专门管理realm数据库的单例TBRealmManager
,其中static let realm = try! Realm()
是拿到默认数据库
import UIKit
import RealmSwift
class TBRealmManager: NSObject {
static let realm = try! Realm()
}
RealmSwift存储、查询、修改以及删除数据时都需要指定数据模型,但是程序员都是懒惰的,代码能少写一行是一行,为了不给每一类模型都单独写增删改查方法,下面方法指定类型全部设计为泛型
,这样,不管你要处理什么数据,直接传其类型即可。
向数据库添加数据方法
class func addModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object)
}
} catch {}
}
测试
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dog1 = Dog()
dog1.name = "大黄"
let dog2 = Dog()
dog2.name = "小黑"
let person = Person()
person.id = 1
person.name = "小明"
person.dogs.append(dog1)
person.dogs.append(dog2)
TBRealmManager.addModel(model: person)
}
}
如果项目跑起来没报错,那么person就已经成功存储到数据库中了,接下来查询一下这个数据
2.4 查
同样的,查询也采用泛型,并增加可选参数filter
,当需要指定查询条件时,可以将对应SQL
语句给其赋值查询
class func queryModel <T> (model: T, filter: String? = nil) -> [T]{
var results : Results<Object>
if filter != nil {
results = realm.objects((T.self as! Object.Type).self).filter(filter!)
}
else {
results = realm.objects((T.self as! Object.Type).self)
}
guard results.count > 0 else { return [] }
var modelArray = [T]()
for model in results{
modelArray.append(model as! T)
}
return modelArray
}
测试
为了测试按条件查询,查询前再插入一个Person对象。
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dog1 = Dog()
dog1.name = "赛虎"
let dog2 = Dog()
dog2.name = "豆豆"
let person = Person()
person.id = 2
person.name = "小王"
person.dogs.append(dog1)
person.dogs.append(dog2)
TBRealmManager.addModel(model: person)
let result = TBRealmManager.queryModel(model: Person())
print("result: ", result)
let result2 = TBRealmManager.queryModel(model: Person(), filter: "name = '小王'")
print("result2: ", result2)
}
}
控制台输出结果:
2.5 改
现在将小明的狗“小黑”改名为“小白”
import UIKit
import RealmSwift
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let result = TBRealmManager.queryModel(model: Person(), filter: "name = '小明'")
print("修改前: ", result)
let model = result.first
let realm = try! Realm()
do {
try realm.write {
model?.dogs.last?.name = "小白"
}
} catch {}
let result2 = TBRealmManager.queryModel(model: Person())
print("修改后结果: ", result2)
}
}
控制台输出结果:
当然也可以单独封装一个改的方法,但是在调用时需要重新创建一个主键一致的模型进行更新,如下:
///调用此方法的模型必须具有主键
class func updateModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object, update: true)
}
}catch{}
}
测试,更改小明的名字为明明
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let result = TBRealmManager.queryModel(model: Person(), filter: "name = '小明'")
print("修改前: ", result)
let model = result.first!
let updateModel = Person()
updateModel.name = "明明"
updateModel.id = model.id
updateModel.dogs = model.dogs
TBRealmManager.updateModel(model: updateModel)
let result2 = TBRealmManager.queryModel(model: Person())
print("修改后结果: ", result2)
}
}
结果:
2.6 删
class func deleteModel <T> (model: T){
do {
try realm.write {
realm.delete(model as! Object)
}
} catch {}
}
测试
删除明明这个数据
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let result = TBRealmManager.queryModel(model: Person(), filter: "name = '明明'")
let model = result.first!
TBRealmManager.deleteModel(model: model)
let result2 = TBRealmManager.queryModel(model: Person())
print("删除后: ", result2)
}
}
结果:
当然,也可以直接删除整张表,实质是查询所有结果并进行删除
///删除某张表
class func deleteModelList <T> (model: T){
do {
try realm.write {
realm.delete(realm.objects((T.self as! Object.Type).self))
}
}catch {}
}
测试:
可以看到,在删除整张表之后,查询到的结果是一个空数组,说明并无查询结果。
好了,时间有点仓促,明天又是一个加班的日子,各位晚安吧
附 TBRealmManager.swift
import UIKit
import RealmSwift
class TBRealmManager: NSObject {
static let realm = try! Realm()
class func addModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object)
}
} catch {}
}
class func queryModel <T> (model: T, filter: String? = nil) -> [T]{
var results : Results<Object>
if filter != nil {
results = realm.objects((T.self as! Object.Type).self).filter(filter!)
}
else {
results = realm.objects((T.self as! Object.Type).self)
}
guard results.count > 0 else { return [] }
var modelArray = [T]()
for model in results{
modelArray.append(model as! T)
}
return modelArray
}
class func deleteModel <T> (model: T){
do {
try realm.write {
realm.delete(model as! Object)
}
} catch {}
}
///删除某张表
class func deleteModelList <T> (model: T){
do {
try realm.write {
realm.delete(realm.objects((T.self as! Object.Type).self))
}
}catch {}
}
///调用此方法的模型必须具有主键
class func updateModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object, update: true)
}
}catch{}
}
}
由于realm线程问题,ream实例,Object实例在哪一个线程创建就需要在哪一个线程使用,所以下面稍微修改了管理类的代码,实际开发时可以根据情况选择创建realm实例的线程
import UIKit
import RealmSwift
class TBRealmManager: NSObject {
static let shared = TBRealmManager()
fileprivate override init() {}
//线程可能会变,此处使用计算属性可以随时更改realm所处线程
var realm: Realm{
return try! Realm()
}
func addModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object)
}
} catch {}
}
func queryModel <T> (model: T.Type, filter: String? = nil) -> [T]{
var results : Results<Object>
if filter != nil {
results = realm.objects((model as! Object.Type).self).filter(filter!)
}
else {
results = realm.objects((model as! Object.Type).self)
}
guard results.count > 0 else { return [] }
var modelArray = [T]()
for model in results{
modelArray.append(model as! T)
}
return modelArray
}
func deleteModel <T> (model: T){
do {
try realm.write {
realm.delete(model as! Object)
}
} catch {}
}
///删除某张表
func deleteModelList <T> (model: T){
do {
try realm.write {
realm.delete(realm.objects((T.self as! Object.Type).self))
}
}catch {}
}
///调用此方法的模型必须具有主键
func updateModel <T> (model: T){
do {
try realm.write {
realm.add(model as! Object, update: true)
}
}catch{}
}
}