반응형
Package Manager
https://github.com/realm/realm-swift.git
글을 엉망진창으로 써놔 추후에 다시 깔끔히 정리해 올릴 예정입니다. 😓
간단 사용법 맛보기
1. AppDelegate에서 realm 생성
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
let realm = try Realm()
} catch {
print("Error initialising new realm \(error)")
}
return true
}
2. 데이터 모델 제작
import RealmSwift
// 슈퍼클라스로 Object 사용, 그 후 Realm의 객체를 정의
class Data: Object {
// Realm을 사용하기 위해선 dynamic 키워드가 필요 (객체를 동적 디스패치를 사용하도록 지시)
// 변수가 변할때 런타임시 모니터링 후 업데이트
// ⭐️ 동적 디스패치는 실제로 Obj-C APId에서 제공 되므로 꼭 @objc키워드 필요
@objc dynamic var name: String = ""
@objc dynamic var age: Int = 0
}
3. 저장
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 데이터 베이스 주소 찾기
print(Realm.Configuration.defaultConfiguration.fileURL)
// 만든 모델 데이터에 값 저장하기
let data = Data()
data.name = "Daniel"
data.age = 20
// Realm으로 모델 값 저장
do {
let realm = try Realm()
try realm.write {
realm.add(data)
}
} catch {
print("Error initialising new realm \(error)")
}
return true
}
4. CRUD (자세한 설명은 아래 예제 참고)
// Create (생성)
// 생성해 둔 객체 Results<T>를 add 값으로 사용
func saveCategorys(category: Results<T>) {
do {
try realm.write {
realm.add(category)
}
} catch {
print("Error save: \(error)")
}
}
// Read (읽기)
// objects는 만들어둔 모델의 타입의 메타타입으로 사용
realm.objects(Model.self)
// Update (업데이트)
// 동작은 Create와 같다.
// 업데이트를 이와같이 컴플리션으로 묶어주면 사용하면 편리해짐
func updateItem(completion: () -> Void) {
do {
try realm.write {
completion()
}
} catch {
print("Error save: \(error)")
}
}
// Delete
func deleteItme(itme: Item) {
do {
try realm.write {
realm.delete(itme)
}
} catch {
print("Error delete: \(error)")
}
}
RelationShip을 이용한 사용법
1. AppDelegate에서 realm 생성
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
let realm = try Realm()
} catch {
print("Error initialising new realm \(error)")
}
return true
}
2. 모델 생성 후 RelationShip 만들기 정방향 모델 역방향 모델
Realm을 사용하기 위해선 dynamic 키워드가 필요 (객체를 동적 디스패치를 사용하도록 지시)
변수가 변할때 런타임시 모니터링 후 업데이트
⭐️ 동적 디스패치는 실제로 Obj-C API에서 제공 되므로 꼭 @objc키워드 필요
- 정방향 모델
import Foundation
import RealmSwift
// 만들고 싶은 모델에 Object 채택, 그 후 Realm의 객체를 정의
class Category: Object {
@objc dynamic var name: String = ""
// 정방향 릴레이션 설정하는 방법 (배열을 만들어준다.)
// List는 Realm에서 일종의 배열이다 (ex. Array<Int> 배열의 타입 같다)
let items = List<Item>()
}
- 역방향 모델
import Foundation
import RealmSwift
class Item: Object {
@objc dynamic var title: String = ""
@objc dynamic var done: Bool = false
// 역방향 릴레이션 설정 방법
// fromType: 정방향 모델의 메타타입으로 설정, property: 정방향 릴레이션에서 만들어준 배열 이름(items)
var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
}
3. 로컬 영역 설정(VC) - CRUD
class CategoryController: UIViewController {
// 로컬영역에서 사용기하기 위해 실행
// 이미 AppDelegate에서 실행시켰으므로 로컬영역에서는 try!로 간단히 사용
let realm = try! Realm()
// 데이터를 읽을 수 있게 Realm의 Results 타입으로 만들어주자
// ⭐️ Results는 자동업데이트 컨테이너이다 (속성이 변할 때 마다 append를 자동으로 수행.)
var categorys: Results<Category>?
// Create (모델을 담는다)
func saveCategorys(category: Category) {
do {
try realm.write({
realm.add(category)
})
} catch {
print("Error save: \(error)")
}
}
// Read
func loadCategorys() {
// 단 한줄이면 되는데 오른손의 타입이 Results<Category> 타입인걸 명심하자
categorys = realm.objects(Category.self)
}
// Update 동작은 Create와 같다
func updateItem(completion: () -> Void) {
do {
try realm.write {
completion()
}
} catch {
print("Error save: \(error)")
}
}
-------------------------------------------------------------------------------------------------
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToItems", sender: self)
}
// 데어터 전달
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destivationVC = segue.destination as! ItemViewController
if let indexPath = tableView.indexPathForSelectedRow {
// 선택택 category의 인덱스로 불러오게 하여 selectCategory 속성을 트리거하게 만듬
destivationVC.selectCategory = categorys?[indexPath.row]
}
}
}
class ItemViewController: UIViewController {
let realm = try! Realm()
var todoItems: Results<Item>?
// 데이터 전달 받기위해
var selectCategory: Category? {
didSet {
loadItems()
}
}
// Create (⭐️ 릴레이션 관계에 있으므로 selectCategory.items에 접근해서 추가하는 방식으로 해야된다)
func saveItem(itmes: Item) {
do {
try realm.write({
selectCategory?.items.append(itmes)
})
} catch {
print("Error save: \(error)")
}
}
// Read
func loadItems() {
// category의 relationship인 itmes를 불러온다
todoItems = selectCategory?.items.sorted(byKeyPath: "title", ascending: true)
}
// Update 동작은 Create와 같다
func updateItem(completion: () -> Void) {
do {
try realm.write {
// 업데이트 조건
completion()
}
} catch {
print("Error update: \(error)")
}
}
// delete
func deleteItme(itme: Item) {
do {
try realm.write {
realm.delete(itme)
}
} catch {
print("Error delete: \(error)")
}
}
// 데이터 베이스 주소 찾기
print(Realm.Configuration.defaultConfiguration.fileURL)
싱글톤으로 CRUD 구현
import UIKit
import RealmSwift
protocol DataBase {
func read<T: Object>(_ object: T.Type) -> Results<T>
func write<T: Object>(_ object: T)
func delete<T: Object>(_ object: T)
func sort<T: Object>(_ object: T.Type, by keyPath: String, ascending: Bool) -> Results<T>
}
final class DataBaseManager: DataBase {
static let shared = DataBaseManager()
private let database: Realm
private init() {
self.database = try! Realm()
}
func getLocationOfDefaultRealm() {
print("Realm is located at:", database.configuration.fileURL!)
}
func read<T: Object>(_ object: T.Type) -> Results<T> {
return database.objects(object)
}
func write<T: Object>(_ object: T) {
do {
try database.write {
database.add(object, update: .modified)
print("New Item")
}
} catch let error {
print(error)
}
}
func update<T: Object>(_ object: T, completion: @escaping ((T) -> ())) {
do {
try database.write {
completion(object)
}
} catch let error {
print(error)
}
}
func delete<T: Object>(_ object: T) {
do {
try database.write {
database.delete(object)
print("Delete Success")
}
} catch let error {
print(error)
}
}
func sort<T: Object>(_ object: T.Type, by keyPath: String, ascending: Bool = true) -> Results<T> {
return database.objects(object).sorted(byKeyPath: keyPath, ascending: ascending)
}
}
// 싱글톤 객체 가져오기
private let database = DataBaseManager.shared
// Realm 파일 위치 가져오기
database.getLocationOfDefaultRealm()
// Create
let task = Shopping(title: inputTextField.text!, createdAt: Date())
database.write(task)
// Read
shoppingList = database.read(Shopping.self)
// Update
let model = RealmManager.shared.read(RealmDataModel.self)[indexPath.row]
RealmManager.shared.update(model) { model in
model.mainLoad = true
}
// Sort
tasks = database.sort(Shopping.self, by: "title")
// Delete
database.delete(self.shoppingList[indexPath.row])
Cheat Sheet
필터링
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
todoItems = todoItems?.filter("title CONTAINS[cd] %@", searchBar.text!).sorted(byKeyPath: "title", ascending: true)
}
컬러 저장할 때는 hexValue로 저장하여 데이터 저장하는 게 편하다.
[iOS/Swift] UIColor (RGB대신 HEX Color 이용하기) 를 참고해 주세요.
view.backgroundColor = UIColor(hexString: <String>)
async를 사용할 때
- 이유는 모르겠지만 realm사용 시 클로저 안에다 비동기처리를 하면 앱이 다운이 된다. 오직 메인스레드에서만 돌려야 한다.
- 아래와 같이 메인으로 감싼 후 사용하면 작동됨
func setupWeatherList() {
DispatchQueue.main.async {
self.read(RealmDataModel.self).forEach { model in
if model.loadMain == true {
Task {
await WeatherManager.shared.eachWeatherData(lat: model.lat, lon: model.lon)
}
}
}
}
}
- Task는 비동기 작업이다. 그러므로 awiat에선 완료된 순서대로 데이터를 받으므로 순서가 보장이 안된다.
- 번거롭더라도 새로운 변수에 담아서 사용해야 한다. (or 새로운 모델을 만들어 사용)
- 동기와 비동기 코드를 순서보장을 받고 싶을 땐 AsyncSquense를 사용하는 것이 좋은 방법 중 하나라고 생각한다.
func setupWeatherList() {
var list: [CLLocation] = []
let weatherData = RealmManager.shared.sort(RealmDataModel.self, by: "date")
weatherData.forEach { result in
list.append(CLLocation(latitude: result.lat, longitude: result.lon))
}
let data = AsyncStream<CLLocation> { continuation in
for location in list {
continuation.yield(location)
}
continuation.finish()
}
Task {
for await location in data {
let coordinate = location.coordinate
await self.eachWeatherData(lat: coordinate.latitude, lon: coordinate.longitude)
}
}
}
반응형
'Xcode > Library' 카테고리의 다른 글
[iOS/Swift] 2. FlexLayout 모든 메서드들을 알아보자! (0) | 2024.01.16 |
---|---|
[iOS/Swift] 1. FlexLayout, PinLayout을 사용해 보자! (0) | 2024.01.15 |
[iOS/Swift] CocoaPod 사용법 (0) | 2022.12.21 |
[iOS/Swift] FireBase 사용법 (0) | 2022.12.20 |
[iOS/Swift] 라이브러리 주소 (0) | 2022.12.20 |