版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.12.21 星期五 |
前言
數(shù)據(jù)的持久化存儲是移動端不可避免的一個問題,很多時候的業(yè)務邏輯都需要我們進行本地化存儲解決和完成,我們可以采用很多持久化存儲方案,比如說
plist
文件(屬性列表)、preference
(偏好設置)、NSKeyedArchiver
(歸檔)、SQLite 3
、CoreData
,這里基本上我們都用過。這幾種方案各有優(yōu)缺點,其中,CoreData是蘋果極力推薦我們使用的一種方式,我已經(jīng)將它分離出去一個專題進行說明講解。這個專題主要就是針對另外幾種數(shù)據(jù)持久化存儲方案而設立。
1. 數(shù)據(jù)持久化方案解析(一) —— 一個簡單的基于SQLite持久化方案示例(一)
2. 數(shù)據(jù)持久化方案解析(二) —— 一個簡單的基于SQLite持久化方案示例(二)
3. 數(shù)據(jù)持久化方案解析(三) —— 基于NSCoding的持久化存儲(一)
4. 數(shù)據(jù)持久化方案解析(四) —— 基于NSCoding的持久化存儲(二)
5. 數(shù)據(jù)持久化方案解析(五) —— 基于Realm的持久化存儲(一)
開始
首先看下寫作環(huán)境
Swift 4.2, iOS 12, Xcode 10
Realm
是一個跨平臺的移動數(shù)據(jù)庫解決方案,專為移動應用程序而設計,可以與iOS項目集成。 與Core Data的封裝不同,Realm不依賴于Core Data甚至是SQLite后端。
本教程將向您介紹iOS上Realm
的基本功能。 在本教程結(jié)束時,您將了解如何鏈接Realm框架,創(chuàng)建模型,執(zhí)行查詢和更新記錄。
這是一個場景:你已經(jīng)接受了國家公園管理局的實習生職位。 您的工作是記錄在美國最大的國家公園中發(fā)現(xiàn)的物種。
您需要助理來記錄您的發(fā)現(xiàn),但由于該機構(gòu)沒有預算來雇用新的,您決定為自己創(chuàng)建一個虛擬助手:一個名為Agents Partner
的應用程序。
在Xcode中打開啟動項目。 目前,該應用程序僅包含使用MapKit
的地圖功能,該功能已在項目中設置。
啟動項目缺少Realm,所以是時候添加它了。
注意:本教程是針對
Realm 3.11.1
編寫的。
安裝Realm的有效方法是使用CocoaPods
。
在starter項目的根目錄中,創(chuàng)建一個名為Podfile
的新文件。 復制以下文本并將其粘貼到新創(chuàng)建的文件中:
platform :ios, '12.0'
use_frameworks!
target 'Agents Partner' do
pod 'RealmSwift'
end
保存并關閉文件。
在終端和項目的根目錄中,運行以下命令:
pod install
這告訴CocoaPods掃描你的Podfile
并安裝你在文件中列出的任何pod。 很簡約!
Realm
可能需要安裝一會,所以請密切關注您的終端。 一旦完成,您將看到底部附近的一條線,Pod installation complete!
!
在Finder中,打開starter項目的根目錄。 請注意CocoaPods
添加的文件夾以及Agents Partner.xcworkspace
。
如果您在Xcode中打開了起始項目,請立即關閉它并通過雙擊該文件打開.xcworkspace
。 當您想要處理項目時,需要打開此文件。
您只需使用CocoaPods設置Realm。 構(gòu)建并運行項目以確保編譯所有內(nèi)容。 如果一切按預期進行,你會看到:
Concepts and Classes Overview - 概念和類概覽
為了更好地理解Realm的作用,下面是一些關于您將在本教程中使用的類的概念和信息:
-
Realm:Realm實例是框架的核心。它是您的基礎數(shù)據(jù)庫的訪問點,如
Core Data managed object context
。您可以使用Realm()
初始值設定項創(chuàng)建實例。 -
Object:這是您的Realm模型。創(chuàng)建模型的行為定義了數(shù)據(jù)庫的
schema
。要創(chuàng)建模型,請將Object
子類化并定義要作為屬性存儲的字段。 -
Relationships:通過聲明要引用的
Object
類型的屬性,可以在對象之間創(chuàng)建一對多關系。您可以通過List
類型的屬性創(chuàng)建多對一和多對多關系。 -
Write Transactions:數(shù)據(jù)庫中的任何操作,如創(chuàng)建,編輯或刪除對象,都必須通過在
Realm
實例上調(diào)用write(_ :)
來在寫入中執(zhí)行。 -
Queries:要從數(shù)據(jù)庫中檢索對象,請使用查詢。最簡單的查詢形式是在
Realm
實例上調(diào)用objects()
并傳入您正在尋找的Object
的類。如果您的數(shù)據(jù)檢索需求更加復雜,您可以使用謂詞,鏈接查詢并對結(jié)果進行排序。 -
Results:
Results
是您從對象查詢返回的自動更新容器類型。它們與常規(guī)數(shù)組Arrays
有許多相似之處,包括下標語法。
通過對Realm的簡要介紹,是時候讓你構(gòu)建項目的其余部分。
Your First Model
從Models
組打開Specimen.swift
并添加以下實現(xiàn):
import Foundation
import RealmSwift
class Specimen: Object {
@objc dynamic var name = ""
@objc dynamic var specimenDescription = ""
@objc dynamic var latitude = 0.0
@objc dynamic var longitude = 0.0
@objc dynamic var created = Date()
}
上面的代碼添加了一些屬性:
name
和samplesDescription
存儲樣本的名稱和描述。 Realm
中的特定數(shù)據(jù)類型(如字符串)必須使用值初始化。 在這種情況下,您使用空字符串初始化它們。
latitude
和 longitude
存儲樣本的坐標。 在這里,您將類型設置為Double
并使用0.0
初始化它們。
created
存儲樣本的創(chuàng)建日期。 Date()
返回當前日期,以便使用該值初始化該屬性。
在Realm中創(chuàng)建第一個模型后,您是否準備好在一個小挑戰(zhàn)中使用這些知識?
標本應分為不同的類別。 挑戰(zhàn)在于自己創(chuàng)建一個Category
模型。 將文件命名為Category.swift
,并為新模型提供名為name
的單個String
屬性。
如果您想檢查您的工作,解決方案如下:
Category.swift
看起來類似如下import Foundation import RealmSwift class Category: Object { @objc dynamic var name = "" }
您有一個Category
模型,您需要以某種方式與Specimen
模型相關聯(lián)。
回想一下上面的說明,聲明您可以通過聲明具有要鏈接的適當模型的屬性來創(chuàng)建模型之間的關系。
打開Specimen.swift
并在其他屬性下面添加以下聲明:
@objc dynamic var category: Category!
這在Specimen
和Category
之間建立了一對多的關系。 這意味著每個Specimen
只能屬于一個Category
,但每個Category
可以有許多Specimens
。
您現(xiàn)在已經(jīng)擁有了基本數(shù)據(jù)模型。 是時候?qū)⒁恍┯涗浱砑拥侥臄?shù)據(jù)庫了!
Adding Records - 添加記錄
當用戶添加新樣本時,他們可以輸入樣本名稱并選擇一個類別。 打開CategoriesTableViewController.swift
。 此視圖控制器在table view
中顯示類別列表,以便用戶可以選擇一個。
在開始編寫代碼以集成Realm
之前,您需要導入RealmSwift
框架。 將以下行添加到文件的頂部,在import UIKit
下面:
import RealmSwift
您將使用一些默認categories
填充此table view
。 這些Category
實例可以存儲在Results
的實例中。
CategoriesTableViewController
現(xiàn)在有一個categories
數(shù)組作為占位符。 在類定義的頂部找到以下代碼:
var categories: [Any] = []
用以下內(nèi)容替換該代碼:
let realm = try! Realm()
lazy var categories: Results<Category> = { self.realm.objects(Category.self) }()
如果要獲取對象,則始終可以定義所需的模型。 在上面的代碼中,首先創(chuàng)建一個Realm
實例,然后通過調(diào)用objects(_:)
來填充categories
,并傳入所需模型類型的類名。
注意:為了簡化本教程中所需的代碼,您正在使用
try!
調(diào)用拋出錯誤的Realm
方法時。 在您自己的代碼中,您應該使用try
和do / catch
來捕獲和處理錯誤。
您希望為用戶提供一些默認類別,以便在應用首次運行時進行選擇。
將以下輔助方法添加到類定義中:
private func populateDefaultCategories() {
if categories.count == 0 { // 1
try! realm.write() { // 2
let defaultCategories =
["Birds", "Mammals", "Flora", "Reptiles", "Arachnids" ] // 3
for category in defaultCategories { // 4
let newCategory = Category()
newCategory.name = category
realm.add(newCategory)
}
}
categories = realm.objects(Category.self) // 5
}
}
以下是每個編號行的內(nèi)容:
- 1) 如果
count
等于0,則表示數(shù)據(jù)庫沒有Category
記錄。 這是第一次運行應用程序時的情況。 - 2) 這將在
realm
上啟動事務,您現(xiàn)在可以將一些記錄添加到數(shù)據(jù)庫中。 - 3) 在這里,您創(chuàng)建默認類別名稱列表,然后迭代它們。
- 4) 對于每個類別名稱,您可以創(chuàng)建一個新的
Category
實例,填充name
并將該對象添加到realm
。 - 5) 您獲取您創(chuàng)建的所有類別并將其存儲在
categories
中。
將以下行添加到viewDidLoad()
的末尾:
populateDefaultCategories()
這會調(diào)用輔助方法在視圖加載時填充測試類別。
現(xiàn)在您有了一些數(shù)據(jù),您將更新table view
數(shù)據(jù)源方法以顯示類別。 查找tableView(_:cellForRowAt :)
并在return cell
之前添加以下內(nèi)容:
let category = categories[indexPath.row]
cell.textLabel?.text = category.name
此實現(xiàn)基于index path
從categories
中檢索類別。 然后,它設置單元格的文本標簽以顯示類別的name
。
接下來,在您添加到CategoriesTableViewController
的其他屬性下面添加此屬性:
var selectedCategory: Category!
您將使用此屬性存儲當前選定的Category
。
找到tableView(_:willSelectRowAtIndexPath :)
并在return indexPath
之前添加以下內(nèi)容:
selectedCategory = categories[indexPath.row]
這會將用戶的選擇存儲在您在上面聲明的屬性selectedCategory
中。
構(gòu)建并運行您的應用程序。
將地圖縮放并平移到有趣的地方,并通過點擊右上角的+
按鈕創(chuàng)建新的注釋。 點擊地圖圖釘將其選中,然后點擊注釋數(shù)據(jù)以編輯詳細信息。 最后,點擊Category
的text field
以查看類別列表,如下所示:
您可以選擇一個類別,但只將其保存到屬性中,而不是保存在數(shù)據(jù)庫中的任何其他位置。 很高興看到應用程序中顯示了類別,但是在數(shù)據(jù)庫中查看記錄總是令人放心。 您可以通過Realm Browser
執(zhí)行此操作。
Introducing the Realm Browser
Realm
包含用于讀取和編輯數(shù)據(jù)庫的Realm Browser
。 Realm數(shù)據(jù)庫格式是專有的,不是人類可讀的。
您可以在此處here下載Realm Browser
。
Working With Realm Browser
在開發(fā)應用程序時了解Realm數(shù)據(jù)庫的存儲位置非常重要 - 您可以使用一個巧妙的技巧來找出它的位置。
打開MapViewController.swift
并將以下行添加到現(xiàn)有import
語句下面的文件頂部:
import RealmSwift
在調(diào)用super.viewDidLoad()
之后,將以下行添加到viewDidLoad()
:
print(Realm.Configuration.defaultConfiguration.fileURL!)
此行將數(shù)據(jù)庫位置打印到調(diào)試控制臺。 這是以后使用Realm Browser
瀏覽數(shù)據(jù)庫的一步。
構(gòu)建并運行您的應用程序,您將看到它在Xcode控制臺中報告數(shù)據(jù)庫的位置。
轉(zhuǎn)到數(shù)據(jù)庫位置的最簡單方法是打開Finder
,按Shift-Command-G
并粘貼應用報告的路徑。
在Finder中打開文件夾后,您可能會看到一個或兩個文件。 其中一個是default.realm
,它是您的數(shù)據(jù)庫文件。 第二個文件可能存在也可能不存在,是default.realm.lock
。 鎖定文件可防止在使用數(shù)據(jù)庫時修改其他應用程序。
如果您尚未下載Realm Browser
,請從App Store下載。 雙擊default.realm
以使用Realm Browser
打開它:
在Realm Browser
中打開數(shù)據(jù)庫后,您會看到Category
旁邊有一個5。 這意味著該類包含五個記錄。 單擊一個類以檢查其中包含的各個字段。
Adding Categories
您現(xiàn)在可以設置邏輯來設置Specimen
的category
。
打開AddNewEntryController.swift
并在現(xiàn)有import語句下面導入RealmSwift framework
:
import RealmSwift
將以下屬性添加到類中:
var selectedCategory: Category!
您將使用它來存儲選定的Category
。
接下來,找到unwindFromCategories(segue :)
并在其中添加以下實現(xiàn):
if segue.identifier == "CategorySelectedSegue" {
let categoriesController = segue.source as! CategoriesTableViewController
selectedCategory = categoriesController.selectedCategory
categoryTextField.text = selectedCategory.name
}
當用戶從您在上一步中設置的CategoriesTableViewController
中選擇一個類別時,將調(diào)用unwindFromCategories(segue :)
。 在這里,您檢索所選類別,將其存儲在selectedCategory
中,并使用類別名稱填寫text field
。
您可以繼續(xù)創(chuàng)建您的第一個Specimen
!
Adding Specimens
仍然在AddNewEntryController.swift
中,再向該類添加一個屬性:
var specimen: Specimen!
此屬性存儲新的specimen
對象。
接下來,將此輔助方法添加到類中:
func addNewSpecimen() {
let realm = try! Realm() // 1
try! realm.write { // 2
let newSpecimen = Specimen() // 3
newSpecimen.name = nameTextField.text! // 4
newSpecimen.category = selectedCategory
newSpecimen.specimenDescription = descriptionTextField.text
newSpecimen.latitude = selectedAnnotation.coordinate.latitude
newSpecimen.longitude = selectedAnnotation.coordinate.longitude
realm.add(newSpecimen) // 5
specimen = newSpecimen // 6
}
}
以下是上面代碼的作用:
- 1) 首先,像以前一樣獲取
Realm
實例。 - 2) 啟動寫入事務以添加新的
Specimen
。 - 3) 創(chuàng)建一個新的
Specimen
實例。 - 4) 分配
Specimen
值。 值來自用戶界面中的輸入文本字段,選定的類別和地圖注釋中的坐標。 - 5) 將新
Specimen
添加到領域。 - 6) 將新
Specimen
分配給Specimen
屬性。
您需要某種驗證器來確保所有字段都填充在您的Specimen
中。 存在AddNewEntryViewController
中的validateFields()
以檢查Specimen
名稱和描述。 由于您已添加了為樣本分配類別的功能,因此您也將檢查該字段。
在validateFields()
中找到如下所示的行:
if nameTextField.text!.isEmpty || descriptionTextField.text!.isEmpty {
修改這一行,如下所示:
if
nameTextField.text!.isEmpty ||
descriptionTextField.text!.isEmpty ||
selectedCategory == nil {
這將驗證是否已填充所有字段以及您是否已選擇類別。
接下來,將以下方法添加到類中:
override func shouldPerformSegue(
withIdentifier identifier: String,
sender: Any?
) -> Bool {
if validateFields() {
addNewSpecimen()
return true
} else {
return false
}
}
在上面的代碼中,您調(diào)用方法來驗證字段。 如果所有內(nèi)容都已填寫,則添加新樣本并返回true
;否則,你返回false
。
建立并運行。 點擊+
按鈕創(chuàng)建一個新樣本。 填寫名稱和說明,選擇一個類別,然后點擊Confirm
將您的Specimen
添加到數(shù)據(jù)庫。
視圖控制器dismiss,但似乎沒有任何事情發(fā)生。 這是怎么回事?
您已將記錄發(fā)布到您的realm
,但您尚未使用新樣本填充地圖。
Retrieving Records
您已將樣本添加到要在地圖上顯示的數(shù)據(jù)庫中。
首先看一下Realm Browser
中更新的數(shù)據(jù)庫:
您將看到填充了其字段的單個樣本,以及MKAnnotation
中的緯度和經(jīng)度。 您還可以看到標本類別的鏈接;這意味著您的一對多Category
關系正在按預期工作。
單擊Specimen
記錄中的Category
以查看Category
記錄本身。
接下來,您將在應用程序中填充地圖。
打開SpecimenAnnotation.swift
并向該類添加屬性:
var specimen: Specimen?
這保存了注釋的Specimen
。
接下來,使用以下內(nèi)容替換初始化程序:
init(
coordinate: CLLocationCoordinate2D,
title: String,
subtitle: String,
specimen: Specimen? = nil
) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
self.specimen = specimen
}
這里的更改是添加一個傳遞Specimen
的選項。 樣本的默認值為nil
,這意味著如果您愿意,可以省略該參數(shù)。 如果沒有標本,應用程序的其余部分仍然可以使用前三個參數(shù)調(diào)用初始化程序。
打開MapViewController.swift
并向該類添加一個新屬性:
var specimens = try! Realm().objects(Specimen.self)
由于您希望在此屬性中存儲標本集合,因此請向Realm
實例詢問Specimen
類型的所有對象。
現(xiàn)在,將以下方法添加到類中:
func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
specimens = try! Realm().objects(Specimen.self) // 2
// Create annotations for each one
for specimen in specimens { // 3
let coord = CLLocationCoordinate2D(
latitude: specimen.latitude,
longitude: specimen.longitude);
let specimenAnnotation = SpecimenAnnotation(
coordinate: coord,
title: specimen.name,
subtitle: specimen.category.name,
specimen: specimen)
mapView.addAnnotation(specimenAnnotation) // 4
}
}
下面進行細分:
- 1) 清除地圖上的所有現(xiàn)有注釋以重新開始。
- 2) 刷新您的
specimens
屬性。 - 3) 遍歷
specimens
并使用樣本的坐標以及其name
和category
創(chuàng)建SpecimenAnnotation
。 - 4) 將每個
samplesAnnotation
添加到MKMapView
。
你需要從某個地方調(diào)用這個方法。 找到viewDidLoad()
并將此行添加到其實現(xiàn)的末尾:
populateMap()
這可確保地圖在視圖控制器加載時顯示樣本。
現(xiàn)在,您將更改注釋以包含樣本名稱和類別。 找到unwindFromAddNewEntry(segue :)
并用以下實現(xiàn)替換該方法:
@IBAction func unwindFromAddNewEntry(segue: UIStoryboardSegue) {
let addNewEntryController = segue.source as! AddNewEntryViewController
let addedSpecimen = addNewEntryController.specimen!
let addedSpecimenCoordinate = CLLocationCoordinate2D(
latitude: addedSpecimen.latitude,
longitude: addedSpecimen.longitude)
if let lastAnnotation = lastAnnotation {
mapView.removeAnnotation(lastAnnotation)
} else {
for annotation in mapView.annotations {
if let currentAnnotation = annotation as? SpecimenAnnotation {
if currentAnnotation.coordinate.latitude == addedSpecimenCoordinate.latitude &&
currentAnnotation.coordinate.longitude == addedSpecimenCoordinate.longitude {
mapView.removeAnnotation(currentAnnotation)
break
}
}
}
}
let annotation = SpecimenAnnotation(
coordinate: addedSpecimenCoordinate,
title: addedSpecimen.name,
subtitle: addedSpecimen.category.name,
specimen: addedSpecimen)
mapView.addAnnotation(annotation)
lastAnnotation = nil;
}
一旦從AddNewEntryController
返回并且有一個新的樣本要添加到地圖中,系統(tǒng)將調(diào)用此方法。 將新樣本添加到地圖時,會獲得通用注釋圖標。 對于您的類別,您希望將該圖標更改為特定于類別的圖標。
在這里,您刪除添加到地圖的最后一個注釋,并將其替換為顯示樣本名稱和類別的注釋。
建立并運行。 創(chuàng)建一些不同類別的新標本,并查看地圖如何更新:
1. A Different View
您可能已經(jīng)注意到地圖視圖左上角的Log
按鈕。 除了地圖之外,該應用程序還有一個基于文本的table view
,列出了所有注釋,稱為Log View
。 接下來,您將使用一些數(shù)據(jù)填充此表視圖。
打開LogViewController.swift
并導入RealmSwift
:
import RealmSwift
然后,用以下內(nèi)容替換specimens
屬性:
var specimens = try! Realm().objects(Specimen.self)
.sorted(byKeyPath: "name", ascending: true)
在上面的代碼中,您將占位符數(shù)組替換為包含Specimens
的Results
,就像在MapViewController
中一樣。 它們將按name
排序。
接下來,在return cell
之前將以下內(nèi)容添加到tableView(_:cellForRowAt :)
:
let specimen = specimens[indexPath.row]
cell.titleLabel.text = specimen.name
cell.subtitleLabel.text = specimen.category.name
switch specimen.category.name {
case "Uncategorized":
cell.iconImageView.image = UIImage(named: "IconUncategorized")
case "Reptiles":
cell.iconImageView.image = UIImage(named: "IconReptile")
case "Flora":
cell.iconImageView.image = UIImage(named: "IconFlora")
case "Birds":
cell.iconImageView.image = UIImage(named: "IconBird")
case "Arachnid":
cell.iconImageView.image = UIImage(named: "IconArachnid")
case "Mammals":
cell.iconImageView.image = UIImage(named: "IconMammal")
default:
cell.iconImageView.image = UIImage(named: "IconUncategorized")
}
此方法使用樣本的name
和category
填充單元格。
構(gòu)建并運行您的應用程序。 點擊Log
,您將在table view
中看到所有輸入的樣本,如下所示:
2. Fetches With Predicates
您希望自己的應用有一個方便的搜索功能。 您的starter項目包含一個UISearchController
實例;您將添加一些特定于您的應用的修改,以使其與Realm
一起使用。
在LogViewController.swift
中,將searchResults
屬性替換為以下內(nèi)容:
var searchResults = try! Realm().objects(Specimen.self)
向類中添加這個方法
func filterResultsWithSearchString(searchString: String) {
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString) // 1
let scopeIndex = searchController.searchBar.selectedScopeButtonIndex // 2
let realm = try! Realm()
switch scopeIndex {
case 0:
searchResults = realm.objects(Specimen.self)
.filter(predicate).sorted(byKeyPath: "name", ascending: true) // 3
case 1:
searchResults = realm.objects(Specimen.self).filter(predicate)
.sorted(byKeyPath: "created", ascending: true) // 4
default:
searchResults = realm.objects(Specimen.self).filter(predicate) // 5
}
}
以下是上述函數(shù)的作用:
- 1) 首先,創(chuàng)建一個謂詞,用于搜索以
searchString
開頭的name
。BEGINSWITH
之后的[c]
表示不區(qū)分大小寫的搜索。 - 2) 然后,從搜索欄中獲取對當前所選范圍索引的引用。
- 3) 如果選擇了第一個分段按鈕,則按名稱升序?qū)Y(jié)果進行排序。
- 4) 如果選擇了第二個按鈕,則按創(chuàng)建日期升序?qū)Y(jié)果進行排序。
- 5) 如果未選擇任何按鈕,請不要對結(jié)果進行排序,按照從數(shù)據(jù)庫返回的順序進行排序。
現(xiàn)在,當用戶與search field
交互時,您實際上將執(zhí)行過濾。 在updateSearchResults(for :)
中,在方法的開頭添加以下兩行:
let searchString = searchController.searchBar.text!
filterResultsWithSearchString(searchString: searchString)
由于搜索結(jié)果table view
調(diào)用相同的數(shù)據(jù)源方法,因此您需要對tableView(_:cellForRowAt :)
進行一些小的更改,以處理主日志表視圖和搜索結(jié)果。 在該方法中,找到分配給specimen
的那一行:
let specimen = specimens[indexPath.row]
刪除它并用以下內(nèi)容替換它:
let specimen = searchController.isActive ?
searchResults[indexPath.row] : specimens[indexPath.row]
此代碼檢查searchController
是否處于活動狀態(tài)。 如果是這樣,它將從searchResults
中檢索樣本。 如果沒有,它會從specimens
中取回樣本。
接下來,當用戶點擊范圍欄中的按鈕時,您將添加一個函數(shù)來對返回的結(jié)果進行排序。
將以下實現(xiàn)添加到scopeChanged(sender :)
:
let scopeBar = sender as! UISegmentedControl
let realm = try! Realm()
switch scopeBar.selectedSegmentIndex {
case 1:
specimens = realm.objects(Specimen.self)
.sorted(byKeyPath: "created", ascending: true)
default:
specimens = realm.objects(Specimen.self)
.sorted(byKeyPath: "name", ascending: true)
}
tableView.reloadData()
在這里,您可以檢查按下的范圍按鈕(A-Z
或Date Added
)并進行排序。 默認情況下,列表將按name
排序。
構(gòu)建并運行您的應用程序。 嘗試一些不同的搜索,看看你得到的結(jié)果。
Updating Records
您已經(jīng)介紹了添加記錄,但是當您想要更新記錄時呢?
如果您點擊LogViewController
中的單元格,您將轉(zhuǎn)到AddNewEntryViewController
,但字段為空。 讓用戶編輯字段的第一步是顯示現(xiàn)有數(shù)據(jù)。
打開AddNewEntryViewController.swift
并將以下輔助方法添加到類中:
func fillTextFields() {
nameTextField.text = specimen.name
categoryTextField.text = specimen.category.name
descriptionTextField.text = specimen.specimenDescription
selectedCategory = specimen.category
}
該方法用樣本數(shù)據(jù)填充用戶界面。 請記住,到目前為止,AddNewEntryViewController
僅用于新標本,因此這些字段始終為空。
接下來,將以下行添加到viewDidLoad()
的末尾:
if let specimen = specimen {
title = "Edit \(specimen.name)"
fillTextFields()
} else {
title = "Add New Specimen"
}
此代碼設置導航標題以指示用戶是否正在添加或更新樣本。 如果它是現(xiàn)有樣本,您還可以調(diào)用輔助方法來填充字段。
您需要一種方法來更新樣本記錄和用戶的更改。 添加以下方法:
func updateSpecimen() {
let realm = try! Realm()
try! realm.write {
specimen.name = nameTextField.text!
specimen.category = selectedCategory
specimen.specimenDescription = descriptionTextField.text
}
}
像往常一樣,該方法從獲取Realm
實例開始,然后其余部分封裝在write()
事務中。 在事務內(nèi)部,您更新數(shù)據(jù)字段。
只需要六行代碼來更新Specimen
記錄!
接下來,當用戶點擊Confirm
時,您將調(diào)用上述方法。 找到shouldPerformSegue(withIdentifier:sender :)
并將addNewSpecimen()
的調(diào)用替換為以下內(nèi)容:
if specimen != nil {
updateSpecimen()
} else {
addNewSpecimen()
}
這會調(diào)用您的方法在適當時更新數(shù)據(jù)。
打開LogViewController.swift
并為prepare(for:sender :)
添加以下實現(xiàn):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "Edit") {
let controller = segue.destination as! AddNewEntryViewController
var selectedSpecimen: Specimen!
let indexPath = tableView.indexPathForSelectedRow
if searchController.isActive {
let searchResultsController =
searchController.searchResultsController as! UITableViewController
let indexPathSearch = searchResultsController.tableView.indexPathForSelectedRow
selectedSpecimen = searchResults[indexPathSearch!.row]
} else {
selectedSpecimen = specimens[indexPath!.row]
}
controller.specimen = selectedSpecimen
}
}
您將選定的樣本傳遞給AddNewEntryController
實例。 if / else
的復雜性是因為取決于用戶是否正在查看搜索結(jié)果而使所選樣本不同。
構(gòu)建并運行您的應用程序。 打開日志視圖,然后點擊Specimen
。 您將看到填充了所有字段并準備編輯的詳細信息。
后記
本篇主要講述了基于Realm的持久化存儲,感興趣的給個贊或者關注~~~