[Realm] 사용기

jess·2023년 4월 12일
0

[Library]

목록 보기
1/1

Realm

  • iOS 관점에서의 DataBase 종류에는 CoreData, Realm, SQLite, FMDB등이 존재한다.
  • 이 중에서 Realm이 선호되는 가장 큰 이유로는 성능, 현업에서 자주 사용됨, 호환성 등이 있다.

Realm 사용방법

  • 관련 파일들을 모아놓기 위해 폴더를 따로 구성하는 것이 좋다.

테이블 설계

final class PhotoData: Object {
    
    @objc dynamic var id = "" // 기본키
    @objc dynamic var date = ""
    @objc dynamic var dateAdded = Date() // 새로 추가한 속성
    @objc dynamic var image: Data? = nil
    @objc dynamic var memo = ""
    
    override static func primaryKey() -> String? {
        return "id"
    }
}
  • 테이블 내에 저장하고 싶은 데이터들에 맞는 데이터 타입을 여기에서 확인하여 테이블을 구현해주는 과정이 필요하다.
  • 데이터 타입 및 옵셔널 가능 여부도 확인하여 작성해준다.
  • 이 때 Primary Key를 설정해주어야 한다.
    • Realm 데이터베이스에서 객체를 식별하기 위해 사용되는 고유 식별자이다.
    • 이 코드에서는 id의 속성이 기본 키로 지정된다. override static func primaryKey() -> String? 메서드는 id 속성을 기본 키로 사용하기 위해 오버라이드 된다.
    • 즉, id 속성은 PhotoData 객체를 고유하게 식별할 수 있는 유일한 값이며, 중복되는 값이 없어야 한다.
    • primaryKey 메서드를 구현하고 id속성을 반환하면, Realm은 id속성을 사용하여 객체를 식별하고 인덱싱한다.
      • 따라서 데이터베이스에서 객체를 조회하거나 수정하거나 삭제할 때 id 값을 사용하여 작업을 수행할 수 있다.

테이블 선언 및 저장

  • RealManager 클래스를 정의한다.
final class RealmManager {
    
// Realm 데이터베이스 객체를 생성한다. 
    let realm = try! Realm()
    
// 싱글턴 객체로, RealmManager 클래스의 인스턴스를 반환한다.
    static let shared = RealmManager()

// PhotoData 객체의 배열 
    var photoList = [PhotoData]()
    
    // MARK: - Create

    func save(photoData: PhotoData, image: UIImage) {
        // 저장할 데이터 객체 생성
        let newData = PhotoData()
        newData.id = UUID().uuidString
        newData.date = photoData.date
        newData.memo = photoData.memo
        newData.image = image.jpegData(compressionQuality: 0.5)
        
        // Realm 데이터베이스에 데이터 저장
        try! realm.write {
            realm.add(newData)
        }
    }
    
    func getData(withId id: String) -> PhotoData? {
        // 기존 데이터 가져오기
        let existingData = realm.objects(PhotoData.self).filter { $0.id == id }.first
        return existingData
    }
    
    
    // MARK: - Read
    
    func fetchAll() -> Results<PhotoData> {
        let results = realm.objects(PhotoData.self).sorted(byKeyPath: "dateAdded", ascending: false)
        return results
    }
    
    func fetch(byDate date: String) -> PhotoData? {
        let predicate = NSPredicate(format: "date == %@", date)
        let results = realm.objects(PhotoData.self).filter(predicate)
        return results.first
    }
    
    // MARK: - Update

    
    func update(photoData: PhotoData, image: UIImage) {
        // 기존 데이터 업데이트
        guard let existingData = getData(withId: photoData.id) else {
            return
        }
        do {
            try realm.write {
                existingData.date = photoData.date
                existingData.memo = photoData.memo
                existingData.image = image.jpegData(compressionQuality: 0.5)
            }
        } catch {
            print("????")
        }

    }
    
    // MARK: - Delete
    
    func delete(photoData: PhotoData) {
        do {
            try realm.write {
                realm.delete(photoData)
            }
        } catch {
            print("Error deleting photoData: \(error)")
            
        }
    }
    
}
  • func save(photoData: PhotoData, image: UIImage)
    • PhotoData 객체를 생성하여 Realm 데이터베이스에 저장한다.
  • func getData(withId id: String) -> PhotoData?
    • 주어진 id에 해당하는 PhotoData 객체를 반환한다.
  • func fetchAll() -> Results<PhotoData>
    • 데이터베이스의 모든 PhotoData 객체를 날짜 추가 순으로 정렬하여 반환한다.
  • func fetch(byDate date: String) -> PhotoData?
    • 주어진 날짜에 해당하는 PhotoData 객체를 반환한다.
  • func update(photoData: PhotoData, image: UIImage)
    • 기존의 PhotoData 객체를 수정한다.
  • func delete(photoData: PhotoData)
    • 데이터베이스에서 주어진 PhotoData 객체를 삭제한다.

저장한 데이터 불러오기

PhotoDetailViewController

@objc func saveButtonTapped() {

...
        if diary != nil {
            newData.id = diary!.id
            let image = photoDetailView.photoImageView.image ?? UIImage()
            **realmManager.update(photoData: newData, image: image)**
        } else {
            // 객체가 존재하지 않으면 새로운 객체로 저장
            newData.id = UUID().uuidString
            
            if let image = photoDetailView.photoImageView.image, image != UIImage(named: "addphoto") {
                     **realmManager.save(photoData: newData, image: image)**
                   
 ...
  • DetailViewController에서 저장 버튼을 누를 때 구현한 함수이다.
  • if-else 구문을 사용하여 diary 객체가 nil인지 확인한다.
    • 만일 다이어리 객체가 nil이 아니면, id 값을 할당하고, photoDetailView.photoImageView.image 값이 nil이 아닌 경우, realmManager.update() 메소드를 사용하여 이미 존재하는 데이터를 업데이트한다.
  • 그러나, diary 객체가 nil인 경우, id값을 UUID로 생성하여 할당한다.
    • photoDetailView.photoImageView.image 값이 nil이 아니며 UIImage(named: "addphoto") 와 같지 않은 경우, (내가 설정해 놓은 기본 이미지) **realmManager.save()** 메소드를 사용하여 새 데이터를 저장한다.

저장한 데이터를 기반으로 collectionView 내용 업데이트

PhotoViewController

// numberOfItems() 메소드를 호출하여 데이터 소스의 총 아이템 수를 가져온다. 
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return viewModel.numberOfItems()
    }
    
// PhotoColllecionViewCell과 같은 셀 클래스를 사용하여 셀 객체를 구성하고, 해당 셀에 대한 데이터를 ViewModel객체에서 가져온다.
// 그런 다음, 가져온 데이터를 사용하여 셀을 구성하고 반환한다. 
// 셀 객체를 반환하기 전에 셀에 데이터를 할당해야 한다. ViewModel 객체에서 가져온 이미지 데이터를 사용하여 UIImageView의 이미지 속성을 업데이트한다.
// 셀이 컬렉션 뷰에 표시되기 전 해당 셀의 이미지가 설정되어있으므로, 데이터소스가 로드되는동안 셀이 비어있는 것을 방지할 수 있다.

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoCollectionViewCell.identifier, for: indexPath) as? PhotoCollectionViewCell,
              let photoData = viewModel.photoData(at: indexPath),
              let imageData = photoData.image else { return UICollectionViewCell() }
        
        cell.imageView.image = UIImage(data: imageData)
        return cell
    }
final class PhotoViewModel {
   
    static let realmManager = RealmManager.shared
    
// RealmManager 객체를 사용하여 저장된 모든 PhotoData를 가져오는 photos 프로퍼티
    var photos: Results<PhotoData>?
    
// RelamManager 객체를 사용하여 photos 프로퍼티를 초기화
    init() {
        self.photos = PhotoViewModel.realmManager.fetchAll()
    }
    
// phots의 총 수를 반환
    func numberOfItems() -> Int {
        return photos?.count ?? 0
    }
    
// IndexPath를 통해 해당하는 PhotoData 객체를 반환
// UICollecionView의 cellForItemAt 메서드에서 사용 
    func photoData(at indexPath: IndexPath) -> PhotoData? {
        return photos?[indexPath.row]
    }
    
}

0개의 댓글