Danny의 iOS 컨닝페이퍼
article thumbnail
Published 2022. 12. 20. 01:44
[iOS/Swift] Realm 사용법 Xcode/Library
반응형

1. Package Manager

<code>
https://github.com/realm/realm-swift.git

 

글을 엉망진창으로 써놔 추후에 다시 깔끔히 정리해 올릴 예정입니다. 😓 

 

2. 간단 사용법 맛보기

1. AppDelegate에서 realm 생성

<swift>
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. 데이터 모델 제작

<swift>
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. 저장 

<swift>
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 (자세한 설명은 아래 예제 참고)

<swift>
// Create (생성) // 생성해 둔 객체 Results<T>를 add 값으로 사용 func saveCategorys(category: Results<T>) { do { try realm.write { realm.add(category) } } catch { print("Error save: \(error)") } }
<swift>
// Read (읽기) // objects는 만들어둔 모델의 타입의 메타타입으로 사용 realm.objects(Model.self)
<swift>
// Update (업데이트) // 동작은 Create와 같다. // 업데이트를 이와같이 컴플리션으로 묶어주면 사용하면 편리해짐 func updateItem(completion: () -> Void) { do { try realm.write { completion() } } catch { print("Error save: \(error)") } }
<swift>
// Delete func deleteItme(itme: Item) { do { try realm.write { realm.delete(itme) } } catch { print("Error delete: \(error)") } }

 

3. RelationShip을 이용한 사용법

1. AppDelegate에서 realm 생성

<swift>
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 만들기 정방향 모델 역방향 모델

<swift>
Realm을 사용하기 위해선 dynamic 키워드가 필요 (객체를 동적 디스패치를 사용하도록 지시) 변수가 변할때 런타임시 모니터링 후 업데이트 ⭐️ 동적 디스패치는 실제로 Obj-C API에서 제공 되므로 꼭 @objc키워드 필요

 

  • 정방향 모델
<swift>
import Foundation import RealmSwift // 만들고 싶은 모델에 Object 채택, 그 후 Realm의 객체를 정의 class Category: Object { @objc dynamic var name: String = "" // 정방향 릴레이션 설정하는 방법 (배열을 만들어준다.) // List는 Realm에서 일종의 배열이다 (ex. Array<Int> 배열의 타입 같다) let items = List<Item>() }

 

  • 역방향 모델
<swift>
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

<swift>
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] } } }
<swift>
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)") } }
<code>
// 데이터 베이스 주소 찾기 print(Realm.Configuration.defaultConfiguration.fileURL)

 

 

4. 싱글톤으로 CRUD 구현

<code>
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) } }
<code>
// 싱글톤 객체 가져오기 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])

 

 

5. Cheat Sheet

5.1. 필터링

<swift>
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { todoItems = todoItems?.filter("title CONTAINS[cd] %@", searchBar.text!).sorted(byKeyPath: "title", ascending: true) }

5.2. 컬러 저장할 때는 hexValue로 저장하여 데이터 저장하는 게 편하다.

[iOS/Swift] UIColor (RGB대신 HEX Color 이용하기)  를 참고해 주세요.

<code>
view.backgroundColor = UIColor(hexString: <String>)

5.3. async를 사용할 때

  • 이유는 모르겠지만 realm사용 시 클로저 안에다 비동기처리를 하면 앱이 다운이 된다. 오직 메인스레드에서만 돌려야 한다.
  • 아래와 같이 메인으로 감싼 후 사용하면 작동됨
<code>
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를 사용하는 것이 좋은 방법 중 하나라고 생각한다.
<code>
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) } } }
반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!