Swift数据持久化之RealmSwift

在之前的swift 数据持久化之CoreData一文中,曾提到过Realm,说会出一篇使用教程,前段时间断断续续一直在Python的道路上探索,时隔5个月,打算在今天把落下的Realm补上。

Realm简介

  • 一个跨平台的移动终端数据库,基于自己的持久化引擎
  • 比 SQLite 和 Core Data 更好更快更易用,可搭配自家RealmBrowser查看数据
  • 完全免费

GitHub地址
官网地址

注:在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.swiftimport 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: 0oldSchemaVersion < 0中的数字+1即可。当然设置更大也行,但是不允许比之前的版本号小。

2.2 数据模型设计

这里举一个一对多模型例子,其他的举一反三
设计一个Person模型,import RealmSwift,继承自Object,让其具有nameid属性,并将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{}
    }
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • Realm是由Y Combinator公司孵化出来的一款可以用于iOS(同样适用于Swift&Objective-...
    小歪子go阅读 2,206评论 6 9
  • 嗯哼嗯哼蹦擦擦~~~ 转载自:https://github.com/Tim9Liu9/TimLiu-iOS 目录 ...
    philiha阅读 4,839评论 0 6
  • 每天去挤地铁,挤公交,看着这来来往往的陌生人,觉得这真是一个奇妙的世界。每个人都穿梭在路上,不知道奔去哪里的目的地...
    小镇人阅读 285评论 2 3
  • 破山中贼易,破心中贼难。 要学会随时观察自己的起心动念, 这样才能更好的管理好自己。
    蒋滴阅读 177评论 0 0