UICollectionView에 대해서 알아보기 6편 (Realm, async, await, Delegate)
드디어 6편이다!
올해 봄, 기본기의 부족함을 너무 크게 느껴서, 기본기부터 다시 돌아보는 시간을 가졌는데, 이번에 Realm과 FelxLayout, PinLayout 등을 학습하면서 확실히 이전보다 더 빠르고 잘(?) 이해가 되는 것 같다.
모든 코드는 SPM으로 빌드가 가능한 상태로 올려드립니다.
(주요내용)
이번에는 Realm과 Delegate를 활용해서 어떻게 처리하는지 알아보고
async, await을 활용해서 클로저를 없애고, RxSwift랑 잘 묶어보자!
🌿 UI 결과물 🌿
셀 내에 업데이트 버튼이 위치한다.
즉, 셀 내에서 CRUD작업 중 UD에 해당하는 작업을 처리한다. (CR은 ViewController가 처리)
우선 모델을 설계했는데, 나는 Note모델 내에 Realm을 달고 CRUD 함수를 모두 구현했다.
따라서 Note 모델과 관련한 데이터 처리는 Note모델을 참조하도록 하면 더 구조가 깔끔하지 않을까 생각했다.
(위의 초록색의 내용은 제 생각입니다! 아직 제대로 클린 아키텍쳐에 대해서 학습하고 적용해 본 경험이 부족합니다.)
Realm의 Note 모델을 같이보자.
🌿 Note 모델
//
// Note.swift
// AppleCollectionView
//
// Created by Hamlit Jason on 2022/09/01.
//
import Foundation
import RealmSwift
class Note: Object {
private static let realm = try! Realm()
/// 고유 id
@Persisted(primaryKey: true) var id: String = UUID().uuidString
/// 기록하는 시간
@Persisted var date: Date = Date()
/// 노트의 아이디
@Persisted var title: String
/// 노트의 내용
@Persisted var content: String
}
extension Note {
/// 랜덤한 노트를 생성
static func createNote(action : (() -> Void)? = nil) {
let title = String.createRandomString(length: 5)
let content = String.createRandomString(length: 30)
let note = Note()
note.title = title
note.content = content
try! realm.write {
realm.add(note)
}
action?()
}
/// 노트에 해당하는 정보를 읽음
static func readNote() -> Results<Note> {
return realm.objects(Note.self)
}
/// 해당 노트를 업데이트
static func updateNote(with note: Note, indexPath: IndexPath) async throws -> Void {
let note = note
// NOTE: - Realm은 이렇게해서 업데이트가 가능하다.
try! realm.write {
print("🎉 Realm의 update는 어떤 스레드에서? \(Thread.current)")
note.content = String.createRandomString(length: 30)
return
}
}
/// 해당 노트를 삭제
static func deleteNote(with note: Note, action : (() -> Void)? = nil) {
try! realm.write {
realm.delete(note)
}
action?()
}
}
Realm을 사용할때 @objc dynamic이라는 포스팅이 많이 보였는데 최신 버전에서는 @Persisted를 사용한다.
// RealmSwift Version 10.10 이상
@Persisted
// RealmSwift 👋 sayGoodBye
@objc dynamic
primaryKey도 그렇고 propertyWrapper를 사용하는게 더 swifty하다!
근데, SwiftUI에서 SceneDelegate의 라이프 사이클을 소괄호에 값을 넣어주던데 그거랑 형태가 닮았다!
SDK 만드는게 3000만큼 재미있는데, 나도 저런 형태 하나 만들어 봐야겠다.
각설하고, 다시 글로 돌아가자면 모델 내에 realm을 인스턴스를 생성하고, 해당 모델의 extension에서 CRUD를 처리한다. 그리고 컴플리션 핸들러를 작성해주는데, 그 이유는 해당 작업이 끝나고 해당 메소드를 호출한 장소에서 수행해야하는 작업이 있을 수도 있기 때문이다.
🌿 async await 부분을 조금 더 자세히 보자.
/// 해당 노트를 업데이트
static func updateNote(with note: Note, indexPath: IndexPath) async throws -> Void {
let note = note
// NOTE: - Realm은 이렇게해서 업데이트가 가능하다.
try! realm.write {
print("🎉 Realm의 update는 어떤 스레드에서? \(Thread.current)")
note.content = String.createRandomString(length: 30)
return
}
}
업데이트 부분에 처음으로 async await을 적용해 보았다.
업데이트는 위의 UI를 보면 알겠지만 Cell 내에서 이벤트를 처리하고 있다.
updateButton.rx.tap
.debug("🌿 update")
.withUnretained(self)
.bind { owner, _ in
Task {
await owner.noteCellDelegate?.noteCell(updateNote: owner.note, indexPath: owner.indexPath)
}
}
.disposed(by: disposeBag)
아 이걸 사용하려고 하니까 Cell의 업데이트를 ViewController로 delegate를 전달해주어야 하는데, 왜냐하면 CollectionView를 업데이트 해줘야 하기 때문!
프로포콜은 다음과 같이 작성해 주었다.
/// NOTE: - AnyObject는 클래스만 사용가능한 프로토콜
protocol NoteCellDelegate: AnyObject {
/// 노트셀에서 노트 삭제
func noteCell(deleteNote note: Note)
/// 노트셀에서 노트 업데이트
func noteCell(updateNote note: Note, indexPath: IndexPath) async
}
네이밍을 어떻게 하는게 최선인지는 아직 가이드라인은 없지만, 최대한 Apple의 내장 프레임워크를 뜯어보면서 구조를 어떻게 잡고 네이밍은 어떻게 했고, 폴더 배치는 어떻게 하는지 찾아보면서 작성하고 있다.
해당 부분은 tableView나 collectionView의 delegate 메소드와 동일한 형태로 작성했다.
근데 update를 보면 async를 사용했다.
그래서 collectionView를 들고있는 NoteViewContoller를 보면 이렇게 작성했다.
extension NoteViewController: NoteCellDelegate {
func noteCell(updateNote note: Note, indexPath: IndexPath) async {
try? await Note.updateNote(with: note, indexPath: indexPath)
self.collectionView.reloadItems(at: [indexPath])
}
func noteCell(deleteNote note: Note) {
Note.deleteNote(with: note) { [unowned self] in
self.collectionView.reloadData()
}
}
}
중요한 것은 collectionView의 reloadData()를 사용하지 않고, reloadItems를 사용하고 있다.
셀 자체를 전부 다시그리는 것 보다 이게 더 성능이 GOOD
사실 cell 하나만 업데이트 하는 메소드가 있다는 것은 몰랐는데, 다른 분 블로그를 읽다가 알게 되었음!
다른 분들 글도 꼼꼼하게 읽자 !_! 정말 유익하다.
마지막으로 async await을 이용해서 프로젝트의 SDK 부분을 전부 바꿔보고 싶다.
(참고)
https://twitter.com/realm/status/1414633070683115527
https://velog.io/@yoonjong/Swift-Realm-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0
https://roniruny.tistory.com/236
https://roniruny.tistory.com/233?category=906090
https://velog.io/@yoonjong/Swift-Realm-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0
https://www.mongodb.com/docs/realm/sdk/swift/crud/filter-data/
https://zeddios.tistory.com/233
https://zeddios.tistory.com/1230
'apple > iOS, UIKit, Documentation' 카테고리의 다른 글
[Realm] Realm CRUD more modern and swifty (0) | 2022.09.04 |
---|---|
[iOS] UICollectionView에 대해서 알아보기 7편 (UICollectionViewDiffableDataSource) (1) | 2022.09.04 |
[iOS] 내가 보려고 기록하는 Realm 구조 설계하기 및 @escaping (0) | 2022.09.02 |
[iOS] FlexLayout을 Cell에서 사용할 때 주의할 점 (0) | 2022.09.02 |
[iOS] Swift random String 생성 (0) | 2022.09.01 |