使用 Realm 和 Swift 创建 ToDo 应用
原文出处: HOSSAM GHAREEB 译文出处:Prayer’s blog(@EclipsePrayer)
智能手机的快速发展的同时,涌现出了很多对开发者友好的开发工具,这些工具不仅使得开发变得更加容易,同时也保证了性能和产品质量。如今,想要在 App Store 占据一席之地,并非易事。想要使得应用易于扩展就更加困难了。当你成功获得百万量级的用户时,应用中的每一个细节都不能放过,并且需要在很短的时间完成对细节的打磨。和数据库打交道,是很多开发者都会面临的一个问题。相信我们每个人都会因为数据库引起的各种问题而头疼不已,对于数据库,我想如今我们只有两种选择:SQLite 和 Core Data。我是 Core Data 的忠实粉丝,它对记录(records)的处理和持久化数据的能力非常强大,但是我意识到,在开发应用的过程中,我在 Core Data 上浪费了太多的时间。最近,我无意中发现了 Realm,一个可以替代 SQLite 和 Core Data 的更好的解决方案。
Realm 是什么?
Realm 是一个跨平台的移动终端数据库,支持 iOS(Swift 和 Objective-C 语言都支持)和 Android。Realm 的目的就是提供比 SQLite 和 Core Data 更好更快的数据库支持。它不仅仅是更好和更快,而且更加易于使用,短短几行代码就可以完成很多操作。Realm 完全免费,你可以随意使用它。Realm 是为移动设备而生的,因为在过去的十年中,移动终端的数据库技术没有任何的革新。现在如果和移动终端的数据库打交道,你只有一种选择,使用 SQLite 或者是底层封装了 SQLite 的技术比如 Core Data。Realm 的目的是更加易用,它并不是一个建立在 SQLite 之上的 ORM,而是一个基于自己的持久化引擎,简单并且快速的面向对象移动数据库。
为什么选择 Realm ?
Realm拥有令人难以置信的速度并且使用起来非常简单,你会发现,无论是想完成数据库的读还是写操作,都只需要短短的几行代码。下面我会列出它的所有优势,并说明为什么 Realm 是你在移动应用上数据库的不二选择:
- 安装简单:在下一个章节——如何使用 Realm 中你会了解到,安装 Realm 会比你想象的更为简单。使用简单的 Cocoapods 命令,你就可以完成所有的安装工作。
- 速度更快:使用 Realm 库操作数据库的速度非常快。它比 SQLite 和 CoreData 都更加快速,这里有它们之间的比较指标作为证据。
- 跨平台:Realm 的数据库文件是跨平台的,可以在 iOS 和 Android 间共享。无论你是使用 Java,Objective-C 或者 Swift,都可以使用相同的抽象模型访问。
- 易于扩展:如果你的移动应用需要处理大量的用户数据记录,数据库的可扩展性就显得十分重要。在开始着手开发应用之前,这一点就应该被考虑在内。Realm 提供很好的可扩展性,在操作大量的数据时,速度也非常之快。选择使用 Realm 可以为你的应用带来更快的速度和更加流畅的用户体验。
- 规范的文档和很好的支持:Realm 提供了大量条理清晰、易于阅读的文档。如果你有任何的问题,你也可以通过 Twitter,Github 或 Stackoverflow 来联系它们获得帮助。
- 可信任:Realm 已经被大量的初创公司和大公司用于它们的移动应用中,像 Pinterest,Dubsmash 和 Hipmunk。
- 免费:除了以上这些极好的优点以外,它还是完!全!免!费!的!
让我们开始动手吧
让我们开始学习 Realm,使用它来构建一个使用 Swift 语言的简单的 iPhone 应用。所完成的 demo 程序是一个简单的 Todo 应用。用户可以增加任务清单,每个任务清单都能够包含多个任务。任务拥有名称,备注,到期日期,可以添加图片,并且拥有一个布尔值来表示该任务是否已经完成。在开始创建 Xcode project 之前,我们需要先配置好 Xcode,安装使用 Realm 所需的工具。
环境要求
请注意需要满足下列要求
- iOS 8 及更高的版本,OS X 10.9 及更高的版本
- Xcode 6.3 及更高版本
- Realm 有两个发行版,一个针对 Swift 2.0,另一个针对 Swift 1.2。我们推荐您使用 Swift 2.0 的版本。当然你也可以使用针对 Swifit 1.2 的版本,但是以后这个版本 Reaml 不会再进行更新支持,所以为了保险起见,使用 Swift 2.0 对应的版本,是个更好的选择。
配置 Xcode 和所需的工具
在开始配置 Xcode 项目之前,请确保运行环境已经正确安装了 CocoaPods,我们将使用它来为项目安装 Realm。如果对 CocoaPods 不熟悉,你可以查看在线的教程,这些教程的材料足够让你明白如何开始使用它。
现在,使用 Single View Application
项目模板创建一个 Xcode 工程,命名为 RealmTasks
或者其他你喜欢的名字。请确保选择 Swift 作为开发语言。之后使用终端进入该工程目录,使用如下命令来初始化 CocoaPods:
Objective-C
1
|
pod init
|
然后使用 Xcode 打开生成的 Podfile 文件,在 target 之后,添加 pod 'RealmSwift'
,修改完之后,应该是下面这个样子:
下面运行 pod install
命令来把 Realm 下载到项目中。完成之后,你会在你的工程文件目录发现一个新生成的 Xcode 工作空间。请确保打开 RealmTasks.xcworkspace 而不是 xcodeproj。打开工作空间后,你会得到下面的情形:
现在就可以在 Xcode 中使用 Realm 啦,但是我们将安装下面的小工具来让使用 Realm 的过程更加容易。
安装 Realm Xcode 插件
Realm 开发团队提供了一个生成 Realm 模型非常有用的工具。为了安装这个插件,我们要使用 Alcatraz。如果你不知道 Alcatraz 是什么的话,这里解释一下,它是一个简单有用的开源包管理工具,它可以让你无需任何配置,自动地为 Xcode 安装插件,文件模板和颜色主题。为了安装 Alcatraz,你只需将以下代码复制到终端中执行,之后重启 Xcode:
Objective-C
1
|
curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/master/Scripts/install.sh | sh
|
然后在 Xcode 中,选择 Window
->Package Manager
,如下:
然后在弹出的窗口中,你可以选择安装你喜欢的插件或者文件模板,在搜索框中你可以搜索你喜欢的插件、配色。在搜索框输入“Realm”,在出现的结果中选择“RealmPlugin”,点击INSTALL
按钮
Realm Browser
给大家介绍的最后一个工具是 Realm 浏览器。这个浏览器可以帮助你阅读和编辑你的.realm
数据库文件。这些文件在应用程序中被创建,在数据库表中保存了所有的数据实体(entities),属性(attributes)和记录(records)。之前我们说过,这些数据库文件可以在不同的平台像 iOS 和 Android 平台间共享。想要下载 最新版本的 Realm 浏览器,请访问 iTunes store。打开 Realm 浏览器,选择 Tools
->Generate demo database
。它会帮你生成 realm 数据库测试文件,你可以使用该浏览器打开和编辑它的内容。当你打开的时候,你会看到像下面的内容:
在 RealmTestClass1 中,它拥有 1000 条记录,显示了多种不同的参数类型。我们会在下节介绍支持的属性类型。
现在,Realm 的所有准备工作都已就绪,那我们开始动手把。
数据库模型类
好戏才刚刚开始。首先我们来创建模型类或者说我们的数据库。为了创建 Realm 数据模型类,你只需要简单地新建一个普通的 Swift 类继承自 Object 就可以了。因为 Realm 数据模型类的基类是 Object,所以 Object 的子类都可以扩展为 Realm 的模型类。在创建类之后,就可以添加属性了。Realm 支持以下多种数据类型:
- Int, Int8, Int16, Int32, 和 Int64
- Boolean
- Float
- String
- NSDate
- NSData
- 继承自 Object 的类 => 作为一对一关系 (Used for One-to-one relations)
- List
Realm 中的 List 可以包含多个 Object 实例,参考上面 demo 数据库的截图,最后一列表示在其他数据表中的存在的一组引用。在和 Realm 模型类打交道的时候,使用的方式和其他 Swift 类一样。例如,你可以添加方法或者遵循指定的协议。
多说无益,来看代码
现在让我们使用之前在 Xcode 中安装的 Realm 插件来新建一个 Realm 类。打开 Xcode,新建文件,选择 Realm:
然后选择 Swift 语言,类名我们输入 Task。会得到如下结果:
现在,可以向 Task 数据模型中添加属性了。
Properties
我们需要在数据模型中添加需要的属性。该例子中,Task 需要有 name (String), createdAt (NSDate), notes (String), 和 isCompleted (Bool)这些属性。添加这些之后,代码应该像下面酱紫:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Task: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var notes = ""
dynamic var isCompleted = false
// Specify properties to ignore (Realm won't persist these)
// override static func ignoredProperties() -> [String] {
// return []
// }
}
|
我们已经为 Task 添加了属性,所有的属性前面都加了 dynamic var
,这使得属性可以被数据库读写。
接下来,我们要创建 TaskList 模型类,用来存储 Task 实例:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
|
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List<Task>()
// Specify properties to ignore (Realm won't persist these)
// override static func ignoredProperties() -> [String] {
// return []
// }
}
|
TaskList 模型类拥有 name,createAt 和一个包含 Task 的 List 属性。需要注意的是:
- List 用来表示一对多的关系:一个 TaskList 中拥有多个 Task
- List 和 Array 在使用上非常相似,所用的方法和访问数据的方式(索引和下标)都相同。List 后标明了数据类型,所包含的所有对象都应该是相同类型的。
- List 是泛型,这也是为什么我们没有在声明前面加上 dynamic 的原因,因为在 Objective-C 运行时无法表示泛型属性。
在 Realm 中创建数据关系非常直观,就像在之前实现中你看到的一对多关系那样。在使用一对一的关系时,我们将使用 Object 类型,来看下面的例子:
Objective-C
1
2
3
4
5
6
7
|
class Person: Object{
dynamic var name = ""
}
class Car: Object{
dynamic var owner:Person?
}
|
上面的例子中,owner 属性表示 Car 和 Person 之间的一对一数据关系。
现在基本的数据模型都已经创建好了。接下来我们会在做一个 ToDo 应用的同时,来讨论 Realm。首先,从这里下载 app,在 Xcode 7 或更高的版本中运行,就像下面这样:
在项目中,我添加了两个视图控制器: TasksViewController 和 TaskListViewController。第一个视图控制器用来显示单个 task,第二个视图控制器用来显示所有的 TaskList。在 list 视图中,点击 + 按钮来添加一个任务清单。选择一个任务清单将会详情视图。你可以在这里添加多个 task。
了解了 demo 的大体思路之后,现在让我们来看看如何向 Realm 数据库中添加一个新的任务清单。为了实现这个功能,需要如下处理:
- 创建 TaskList 实例对象,并将其保存到 Realm 数据库中
- 向数据库中查询 list 数据,来更新 UI
为了在 Realm 中保存数据,你只需要实例化继承自 Object 的数据模型类,然后将对象写入到 Realm 中,下面是示例代码:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
|
let taskListA = TaskList()
taskListA.name = "Wishlist"
let wish1 = Task()
wish1.name = "iPhone6s"
wish1.notes = "64 GB, Gold"
let wish2 = Task(value: ["name": "Game Console", "notes": "Playstation 4, 1 TB"])
let wish3 = Task(value: ["Car", NSDate(), "Auto R8", false])
taskListA.tasks.appendContentsOf([wish1, wish2, wish3])
|
通过实例化 TaskList 类,我们创建了一个任务清单,之后设置了它的属性。随后我们创建了 3 个 Task 类型的对象。这里我们演示了创建 Realm 对象的三种途径:
- wish1 的实例化方式:简单的实例化 Realm 类,然后设置属性
- wish2 的实例化方式:传入一个字典,字典中的 key 为属性名,值为要设置的值
- wish3 的实例化方式:使用数组传入的方式。数组中值的顺序需要和模型类中的声明顺序一致。
嵌套对象
在 Realm 中还可以是使用嵌套的方式来创建对象。在一对一关系和一对多关系的时候,你可以使用这种方式,这时候,一个类型对象的初始化需要一个或多个另一个类型的对象。面临这种情况的时候,你可以选择上面的第二或者第三种方法,使用一个字典或者一个数组来表示一个对象:
Objective-C
1
|
let taskListB = TaskList(value: ["MoviesList", NSDate(), [["The Martian", NSDate(), "", false], ["The Maze Runner", NSDate(), "", true]]])
|
在上面的代码中,我们创建了一个电影清单,并设置了清单名称、创建时间和清单内容,清单内容包括多个 task。每个 task 使用数组的方式来创建,例如 [“The Maze Runner”, NSDate(), “”, true]
表示一个task,内容分别对应了名称,创建时间,备注和是否已经完成。
使用 Realm 持久化对象
现在你应该知道了如何在 Realm 中创建和使用对象,但是为了能够在应用程序重新启动的时候使用这些数据,需要使用写事务将它们持久化到 Realm 的数据库中。当使用 Realm 来持久化数据的时候,只要这些对象已经存储成功,你可以在任何线程中获取这些对象。一个 Realm 实例表示一个 Realm 数据库。可以像下面一样实例化它:
Objective-C
1
|
let uiRealm = try! Realm()
|
我们常常将上面这行代码写在 AppDelegate.swift
文件的顶端(译者注:类之外,全局变量),这样就可以在整个项目中获得该对象的引用。之后便可以很方便地调用它的读和写方法:
Objective-C
1
2
3
|
uiRealm.write { () -> Void in
uiRealm.add([taskListA, taskListB])
}
|
首先,uiRealm 对象已经在 AppDelegate 中创建,在整个 app 中都可以使用。Realm 对象在每个线程中都应该只被创建一次,因为它不是线程安全的,不能在不同的线程中共享。如果你想要在另一个线程中执行写操作,那么就需要创建一个新的 Realm 对象。我将这个实例命名为 uiRealm
,就是因为它应该只在 UI 线程中被使用。
现在让我们回到我们的 app 中,我们需要在用户点击 Create 按钮的时候保存任务列表。在TasksViewController
的displayAlertToAddTask
方法中,我们有一个 createAction
对象:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
let createAction = UIAlertAction(title: doneTitle, style: UIAlertActionStyle.Default) { (action) -> Void in
let taskName = alertController.textFields?.first?.text
if updatedTask != nil{
// update mode
uiRealm.write({ () -> Void in
updatedTask.name = taskName!
self.readTasksAndUpateUI()
})
}
else{
let newTask = Task()
newTask.name = taskName!
uiRealm.write({ () -> Void in
self.selectedList.tasks.append(newTask)
self.readTasksAndUpateUI()
})
}
}
|
在上面的代码中,我们从 TextField 中获取到任务名称,调用 Realm 的鞋方法来保存任务列表。
请注意,当你同时进行多个写操作的时候,他们会相互阻塞,阻塞住他们所运行的线程。所以应当考虑在 UI 之外的线程中来进行操作,另外需要注意的是,在进行写事务的时候,读操作并不会造成阻塞。这非常有用,尤其是当你在后台进行写操作的时候,用户可能会在不同界面切换,而这时候可以进行读操作。
检索数据
现在你已经学会了如何在 Realm 中写数据,下面我们来看看如何检索数据。在 Realm 中检索数据的方式非常直观。Realm 提供了很多选项来过滤出你想要的数据。在 Realm 中进行查找操作的时候,它将会返回一个 Results 对象。可以把 Results 简单地当做是 Swift 的数组,因为它们的接口非常类似。
当得到 Results 实例的时候,已经从磁盘中直接获取到了数据。对这些数据的任何操作(使用事务)将会影响到磁盘上的数据。在 Realm 中来检索数据,只需要调用对象的方法,并将类名作为参数传进去。让我们看看如何使用这种方式来读取 TaskLists 并更新 UI:
我们在 TasksListsViewController
中定义了该属性:
Objective-C
1
|
var lists : Results<TaskList>!
|
然后实现了 readTasksAndUpdateUI
方法:
Objective-C
1
2
3
4
5
|
func readTasksAndUpdateUI(){
lists = uiRealm.objects(TaskList)
self.taskListsTableView.setEditing(false, animated: true)
self.taskListsTableView.reloadData()
}
|
在 tableView(_:cellForRowAtIndexPath:_)
方法中,我们将显示列表的名称,还有每个列表内的任务个数:
Objective-C
1
2
3
4
5
6
7
8
9
10
|
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("listCell")
let list = lists[indexPath.row]
cell?.textLabel?.text = list.name
cell?.detailTextLabel?.text = "\(list.tasks.count) Tasks"
return cell!
}
|
代码逻辑非常直观吧。最后需要做的就是在 viewWillAppear
中调用 readTasksAndUpdateUI
方法,来确保总是显示数据更新之后的视图。
Objective-C
1
2
3
|
override func viewWillAppear(animated: Bool) {
readTasksAndUpdateUI()
}
|
上面展示了如何使用 Realm 来进行任务列表的读写操作。接下来,我们来看看如何进行数据更新和删除操作。在开始之前,先来看看项目中的修改和删除不分的相关代码。
首先在 TaskListsViewController
中,我们用一个布尔值 isEditingMode
来表示是在正常状态还是编辑状态:
Objective-C
1
|
var isEditingMode = false
|
当 Edit 按钮被点击的时候,将会调用 didClickOnEditButton
方法:
Objective-C
1
2
3
4
|
@IBAction func didClickOnEditButton(sender: UIBarButtonItem) {
isEditingMode = !isEditingMode
self.taskListsTableView.setEditing(isEditingMode, animated: true)
}
|
这个方法会使用 table view 的 setEditing
方法来开启或禁用 UITableView 的编辑模式。在表格视图中,默认的编辑操作是删除,但是从 iOS 8.0 开始,增加了一个 editActionsForRowAtIndexPath
方法来自定义一些操作,这些操作在在用户滑动表格 cell 的时候出现。
我们将使用该方法来添加两个功能,删除和编辑,代码如下:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Destructive, title: "Delete") { (deleteAction, indexPath) -> Void in
//Deletion will go here
let listToBeDeleted = self.lists[indexPath.row]
uiRealm.write({ () -> Void in
uiRealm.delete(listToBeDeleted)
self.readTasksAndUpdateUI()
})
}
let editAction = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: "Edit") { (editAction, indexPath) -> Void in
// Editing will go here
let listToBeUpdated = self.lists[indexPath.row]
self.displayAlertToAddTaskList(listToBeUpdated)
}
return [deleteAction, editAction]
}
|
这里我们使用 UITableViewRowAction
添加了两个操作,方法中定义了操作的 style
,title
和 handler
。当在滑动 cell 或者以其他方式进入编辑模式的时候,会像下面这样:
以上就是在进行删除和更新操作时候的 UI 代码逻辑。
删除对象
想要从 Realm 数据库中删除对象或者数据,你只需要调用 Realm 对象的 delete
方法,同时将该对象作为参数传入。当然,这些操作会在写事务中完成。来看一下下面的代码的工作方式,我们从 Realm 数据库中删除了一个任务列表:
Objective-C
1
2
3
4
5
|
let listToBeDeleted = self.lists[indexPath.row]
uiRealm.write({ () -> Void in
uiRealm.delete(listToBeDeleted)
self.readTasksAndUpdateUI()
})
|
在删除之后,我们调用了 readTasksAndUpdateUI
方法来读取数据并更新 UI。
在 Realm 中,还有一个方法叫做 deleteAll
,它允许你删除数据库中所有 class 的所有数据。如果你想为当前用户持久化数据,但是在他退出登录的时候抹掉所有的相关数据,这个方法将会十分有用。
Objective-C
1
2
3
|
uiRealm.write({ () -> Void in
uiRealm.deleteAll()
})
|
更新 Objects
在 Realm 中有多种方式可以来更新 object,但是这些方法都应该在写事务中完成。下面我们来看一些更新对象的方式。
使用 properties
你可以通过直接在 Realm 的写闭包中设置 property 的值来更新数据。例如,在 TasksViewController
中,我们可以简单地设置属性值来更新任务的状态信息:
Objective-C
1
2
3
|
uiRealm.write({ () -> Void in
task.isCompleted = true
})
|
使用主键
Realm 支持将某个 string 或 int 类型的属性设置为主键。当使用 add()
方法来创建 Realm 对象时,如果有相同主键的对象存在,就会更新这个对象的值。下面是示例代码:
Objective-C
1
2
3
4
5
6
7
8
9
|
let user = User()
user.firstName = "John"
user.lastName = "Smith"
user.email = "example@example.com"
user.id = 1
// Updating User with id = 1
realm.write {
realm.add(user, update: true)
}
|
这里的 id 属性为主键。如果 id 为 1 的用户存在,Realm 会更新相应的对象。如果不存在,Realm 将会把该对象存入数据库中。
使用 KVC (Key-Value Coding)
如果你是 iOS 开发的老手,那么对 Key-Value Coding 肯定不会陌生。Realm 的类型,像 Object,Results 和 List,都可以使用 Key-Value Coding。该特性可以帮助你在运行时设置或更新属性值。另外一个支持 KVC 的好处是,可以在无需遍历每个对象的情况下,批量更新对象数据。这么说你可能不是很理解,我们来看个例子:
Objective-C
1
2
3
4
|
let tasks = uiRealm.objects(Task)
uiRealm.write { () -> Void in
tasks.setValue(true, forKeyPath: "isCompleted")
}
|
在上面的代码中,使用查询语句来请求所有的 Task 对象,之后将所有得到的对象的 isCompleted
属性设置为 true。可以看出,将 Realm 中的所有 tasks 标记为已完成,仅仅只用了一行代码。
让我们回过头来看看我们的 ToDo 应用。如果仔细观察 displayAlertToAddTaskList
方法,你会看到如下代码片段:
Objective-C
1
2
3
4
5
|
// update mode
uiRealm.write({ () -> Void in
updatedList.name = listName!
elf.readTasksAndUpdateUI()
})
|
当用户遍历列表名称的时候,上面的代码会被调用。通过设置 name 属性的方式,就更新了数据库中的内容。
显示任务列表
我们将一起看看 TaskListViewController
的大部分代码。现在让我们打开 TasksViewController
来看看,这个视图控制器用来显示任务清单中的任务项。视图控制器拥有一个 UITableView, 该视图有两个 section:完成的任务和未完成的任务。在 TasksViewController
中,有如下属性:
Objective-C
1
2
3
|
var selectedList : TaskList!
var openTasks : Results<Task>!
var completedTasks : Results<Task>!
|
selectedList
用来存储 TaskListsViewController
传递过来的选中的任务列表。为了将完成和未完成的任务分开,声明了两个属性,openTasks
和 completedTasks
。为了过滤出不同的任务完成状态,我们将使用 Realm 的方法 filter()
。在解释该函数如何工作之前,让我们先来看看如何在代码中使用它:
Objective-C
1
2
3
4
5
6
7
|
func readTasksAndUpateUI(){
completedTasks = self.selectedList.tasks.filter("isCompleted = true")
openTasks = self.selectedList.tasks.filter("isCompleted = false")
self.tasksTableView.reloadData()
}
|
在上面的方法中,我们使用 filter
方法来过滤 results
。Realm 提供了 filter()
方法来过滤数据。该方法可以被 List、Result 和 Object 对象调用。方法会返回过满足滤条件参数的特定对象。你可以把 filter
当做 NSPredicate
。基本上来说,你可以认为这两者差不多。就像上面的代码一样,你同样可以使用 string 作为参数创建一个 NSPredicate
对象,并把它作为参数传给 filter
方法。
让我们来看另外一个例子:
Objective-C
1
2
3
4
5
6
|
// using predicate string
var redCars = realm.objects(Car).filter("color = 'red' AND name BEGINSWITH 'BMW'")
// using NSPredicate
let aPredicate = NSPredicate(format: "color = %@ AND name BEGINSWITH %@", "red", "BMW")
redCars = realm.objects(Car).filter(aPredicate)
|
在上面的代码中,我们使用 filter
方法来过滤 color
为 red,并且 name
以 “BMW” 开头的对象。第一行代码使用 string 作为参数来进行过滤。另外,你也可以使用 NSPredicate 获得同样的效果。下面的表格总结了 filter 方法的大部分常用操作:
排序
既然我们谈到了 Realm 数据库的基本操作,在本教程结束之前,我还想给大家介绍另外一个特性。排序功能,这是 Realm 提供了另一个特别有用的特性。对于 List 和 Result 对象,你可以调用方法 sorted(“排序标准”)
来将一组数据进行排序。让我们来看看如何在任务列表中使用该方法让任务列表以字母表或者创建时间先后顺序排序。首先,在 UI 中,我们增加了一个 segment control,将会根据选择的情况来进行对应的排序。
根据不同的选择来进行排序,代码逻辑如下:
Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
|
@IBAction func didSelectSortCriteria(sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
// A-Z
self.lists = self.lists.sorted("name")
}
else{
// date
self.lists = self.lists.sorted("createdAt", ascending:false)
}
self.taskListsTableView.reloadData()
}
|
总结
Realm 是一个非常简单易用,直观的本地数据库解决方案。Realm 提供了很好的可扩展性,只用很少的几行代码就可完成操作。对于大部分的应用甚至是游戏来说,我觉得如果需要使用数据库的话,Realm 值得尝试。
下一步?
学习了本教程,你应该可以在项目中使用 Realm 来进行增删改查等基本的操作。Realm 还具有一些高阶特性,值得深入学习。最好的学习资料就是 Realm 网站的官方文档,Realm 的小伙伴把文档写得非常赞!
如果想要教程 ToDo 应用的的完整代码,你可以在这里下载
如果对代码有任何的问题,都欢迎留言反馈,我们会很乐意帮助到您。QQ技术交流群290551701
使用 Realm 和 Swift 创建 ToDo 应用的更多相关文章
- swift:创建表格UITableView
用swift创建单元格和用iOS创建单元格形式基本相同,就是语法上有些异样.swift中调用成员方法不再使用[ ]来发送消息,而是使用.成员方法的形式调用成员函数.这种格式非常类似于java中的点成员 ...
- 使用OC和swift创建系统自带的刷新界面
使用OC和swift创建系统自带的刷新界面 一:swift刷新界面代码: import UIKit class ViewController: UITableViewController { // 用 ...
- OC与Swift创建pod
Cocoa pods 是iOS最常用的类库管理工具 OC的使用 删除源 sudo gem sources -r https://rubygems.org/ 添加源(使用淘宝的镜像,记住要用 ...
- swift 创建单例模式
一.意图 保证一个类公有一个实例,并提供一个访问它的全局访问点. 二.使用场景 1.使用场景 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时 当这个唯一实例应该是通过子类化可扩展的,并且 ...
- swift 创建桥接头文件
1,创建iOS 下source模块里的header file 文件,这个文件要注意命名的规则:xxx-Brigding-Header.h 2,到build setting 里 Swift Compil ...
- [译] 用 Swift 创建自定义的键盘
本文翻译自 How to make a custom keyboard in iOS 8 using Swift 我将讲解一些关于键盘扩展的基本知识,然后使用iOS 8 提供的新应用扩展API来创建一 ...
- 你会用swift创建复杂的加载动画吗(1)
时至今日,iOS 应用商店已经拥有超过了140万 应用,让你自己的应用脱颖而出确实是个不小的挑战.不过,在你的应用掉入默默无闻的大黑洞之前,你拥有一个小小的机遇窗,它能帮你吸引用户的注意. AD: 时 ...
- Swift 书面 ToDo App
下面的代码是使用的全部Xcode Version 6.0.1 (6A317)书面. 因为当使用团队开发stroyboard在并购的诸多不便的时间,所有或使用.xib该文件准备ToDo App. 想要实 ...
- [Swift]创建CoreData的两种方式
一.CoreData介绍 CoreData主要分为两部分: 上层是模型层,模型层有NSManagedObjectContext上下文管理着, 底层则是由SQLite实现的持久化部分,通过NSPersi ...
随机推荐
- bash命令中的两个横
它是一种标记.命令中的连续的两个横表明选项已经结束了,两个横后面的内容就是参数了,不再是选项了.
- UDP 端到端
创建发送端 1.建立DatagramSocket对象,该端点建立,系统会随机分配一个端口,如果不想随机分配,可手动指定. 2.将数据进行packet封装,必须指定目的地址和端口. 3.通过socket ...
- Codility经典算法题之九:MissingInteger
Task description: This is a demo task. Write a function: that, given an array A of N integers, retur ...
- vue中导出Excel表格
项目中我们可能会碰到导出Excel文件的需求,一般后台管理系统中居多,将table中展示的数据导出保存到本地.当然我们也可以通过一些处理来修改要导出的数据格式,具体需求具体对待. 1.首先我们需要安装 ...
- RedisCluster集群原理
主从复制,数据值每个服务器都存了. 针对redis集群的解决方案, 连接这个集群,不用在乎Master了 6台redis 1.why use Redis? 减轻数据库访问压力 2.持久化 RDB(间隔 ...
- cassandra cpp driver中bind list——用cass_statement_bind_collection函数
CassError insert_into_collections(CassSession* session, const char* key, const char* items[]) { Cass ...
- MyBatis学习 之 四、动态SQL语句
有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的,也许我们需要参数为空时,是查出全部的信息.使用Oracle的序列.mysq ...
- HTML中的align和valign这两个属性
转自:https://www.douban.com/note/325833958/ align和valign属性均是规定表格相对于周围元素的对齐方式,区别就在于: 1.align属性趋向于左右对齐,其 ...
- BZOJ1787 meet
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1787 水题,但是结论很有趣. 题目求的是距离三个点之和最小的点. 这个很显然是在三个LCA上, ...
- Vue HTML5 History 模式
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载. 如果不想要很丑的 hash,我们可以用路由的 his ...