관리 메뉴

오늘도 더 나은 코드를 작성하였습니까?

Realm kotlin React to Changes 본문

카테고리 없음

Realm kotlin React to Changes

hik14 2022. 7. 26. 15:59

모든 최신 앱은 데이터 변경이 시작된 위치에 관계없이 데이터가 변경될 때 대응할 수 있어야 합니다.

 

사용자가 list에 새 item을 추가하면 UI를 업데이트하거나, 알림을 표시하거나 메시지를 기록할 수 있습니다.

누군가 해당 list 업데이트하면 시각적 상태를 변경하거나, 네트워크 요청을 시작하고 싶을 수 있습니다.

마지막으로 누군가가 list item 삭제하면 UI에서 제거하고 싶을 것입니다.

 

Realm의 notification 시스템을 사용하면 변경을 일으킨 write transaction과 상관없이 데이터 변경을 감시하고 이에 대응할 수 있다

 

* Kotlin SDK의 frozen 아키텍처는 notification을 훨씬 더 중요하게 만듭니다.

Kotlin SDK에는 자동으로 업데이트되는 Live 객체가 없기 때문에 notification을 사용하여 UI와 데이터 레이어를 동기화 상태로 유지합니다.

 

이벤트에 대한 변경 사항을 구독할 수 있습니다.

 

RealmObject

class Character(): RealmObject {
    @PrimaryKey
    var name: String = ""
    var species: String = ""
    var age: Int = 0
    constructor(name: String, species: String, age: Int) : this() {
        this.name = name
        this.species = species
        this.age = age
    }
}

class Fellowship() : RealmObject {
    @PrimaryKey
    var name: String = ""
    var members: RealmList<Character> = realmListOf()
    constructor(name: String, members: RealmList<Character>) : this() {
        this.name = name
        this.members = members
    }
}

Data

val config = RealmConfiguration.Builder(setOf(Fellowship::class, Character::class))
    .name(realmName)
    .build()
val realm = Realm.open(config)


val frodo = Character("Frodo", "Hobbit", 51)
val samwise = Character("Samwise", "Hobbit", 39)
val aragorn = Character("Aragorn", "Dúnedain", 87)
val legolas = Character("Legolas", "Elf", 2931)
val gimli = Character("Gimli", "Dwarf", 140)
val gollum = Character("Gollum", "Hobbit", 589)

val fellowshipOfTheRing = Fellowship(
    "Fellowship of the Ring",
    realmListOf(frodo, samwise, aragorn, legolas, gimli))


realm.writeBlocking{
    this.copyToRealm(fellowshipOfTheRing)
    this.copyToRealm(gollum) // not in fellowship
}
realm.close()

Register a Query Change Listener

Realm 내의 모든 query에 알림 핸들러를 등록할 수 있습니다. 

 

asFlow()를 사용하여 쿼리에서 Kotlin Flow를 만듭니다. 그런 다음 collect() 메서드를 사용하여 해당 Flow의 이벤트를 처리합니다.

 

UpdatedResults 유형의 이벤트는 아래 속성을 사용하여 쿼리와 일치하는 개체에 대한 모든 변경 사항을 기록합니다.

 

Property
Type
Description
insertions
IntArray
이 버전에 추가된 새 컬렉션의 인덱스입니다.
insertionRanges
Array<ListChangeSet.Range>
이 버전에 추가된 새 컬렉션의 인덱스 범위입니다.
changes
IntArray
이 버전에서 수정된 새 컬렉션의 개체 인덱스입니다.
changeRanges
Array<ListChangeSet.Range>
이 버전에 수정된 새 컬렉션의 인덱스 범위입니다.
deletions
IntArray
이 컬렉션에서 제거된 컬렉션의 이전 버전에 있는 인덱스입니다.
deletionRanges
Array<ListChangeSet.Range>
이 컬렉션에서 제거된 컬렉션의 이전 버전에 있는 인덱스 범위
list
RealmResults<T as RealmObject>
변경 사항에 대해 모니터링되는 결과 수집
// Listen for changes on whole collection
val characters = realm.query(Character::class)

// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
    // create a Flow from that collection, then add a listener to the Flow
    val charactersFlow = characters.asFlow()
    val subscription = charactersFlow.collect { changes: ResultsChange<Character> ->
        when (changes) {
            // UpdatedResults means this change represents an update/insert/delete operation
            is UpdatedResults -> {
                changes.insertions // indexes of inserted objects
                changes.insertionRanges // ranges of inserted objects
                changes.changes // indexes of modified objects
                changes.changeRanges // ranges of modified objects
                changes.deletions // indexes of deleted objects
                changes.deletionRanges // ranges of deleted objects
                changes.list // the full collection of objects
            }
            else -> {
                // types other than UpdatedResults are not changes -- ignore them
            }
        }
    }
}
// Listen for changes on RealmResults
val hobbits = realm.query(Character::class, "species == 'Hobbit'")
val hobbitJob = CoroutineScope(Dispatchers.Default).launch {
    val hobbitsFlow = hobbits.asFlow()
    val hobbitsSubscription = hobbitsFlow.collect { changes: ResultsChange<Character> ->
        // ... all the same data as above
    }
}

Register a RealmObject Change Listener

Realm 내의 특정 개체에 대한 notification handler 를 등록할 수 있습니다.

Realm은 객체의 속성이 변경될 때 핸들러에 알립니다.

단일 객체에 변경 리스너를 등록하려면 realm.query.first()를 사용하여 RealmSingleQuery를 가져옵니다.

 

asFlow()를 사용하여 해당 쿼리에서 flow 생성합니다. 

Handler 다음 아래 유형을 사용하여 객체 변경 사항을 전달하는 SingleQueryChange 개체를 수신합니다

Subtype
Properties
Notes
UpdatedObject
changedFields, obj
필드 이름을 isFieldChanged()에 전달하여 해당 필드가 변경되었는지 확인합니다.
DeletedObject
obj
obj는 항상 객체의 최신 버전을 반영하므로 항상 이 하위 유형에서 null 값을 반환합니다.
// query for the specific object you intend to listen to
val frodo = realm.query(Character::class, "name == 'Frodo'").first()
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
    val frodoFlow = frodo.asFlow()
    frodoFlow.collect { changes: SingleQueryChange<Character> ->
        when (changes) {
            is UpdatedObject -> {
                changes.changedFields // the changed properties
                changes.obj // the object in its newest state
                changes.isFieldChanged("name") // check if a specific field changed in value
            }
            is DeletedObject -> {
                // if the object has been deleted
                changes.obj // returns null for deleted objects -- always reflects newest state
            }
        }
    }
}

Register a RealmList Change Listener

RealmObject 내의 RealmObject List에 알림 핸들러를 등록할 수 있습니다.

목록 항목이 변경되면 Realm이 핸들러에 알립니다.

먼저 asFlow()를 사용하여 목록에서 Kotlin Flow를 만듭니다. 그런 다음 collect() 메서드를 사용하여 해당 Flow의 이벤트를 처리합니다. UpdatedList 유형의 이벤트는 다음 속성을 사용하여 목록에 대한 모든 변경 사항을 기록합니다.
Property
Type
Description
insertions
IntArray
이 버전에 추가된 새 컬렉션의 인덱스입니다.
insertionRanges
Array<ListChangeSet.Range>
이 버전에 추가된 새 컬렉션의 인덱스 범위입니다.
changes
IntArray
이 버전에서 수정된 새 컬렉션의 개체 인덱스입니다.
changeRanges
Array<ListChangeSet.Range>
이 버전에서 수정된 새 컬렉션의 인덱스 범위입니다.
deletions
IntArray
이 컬렉션에서 제거된 컬렉션의 이전 버전에 있는 인덱스입니다.
deletionRanges
Array<ListChangeSet.Range>
이 컬렉션에서 제거된 컬렉션의 이전 버전에 있는 인덱스 범위입니다.
list
RealmResults<T as RealmObject>
변경 사항에 대해 모니터링되는 결과 수집.
// query for the specific object you intend to listen to
val fellowshipOfTheRing = realm.query(Fellowship::class, "name == 'Fellowship of the Ring'").first().find()!!
val members = fellowshipOfTheRing.members
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
    val membersFlow = members.asFlow()
    membersFlow.collect { changes: ListChange<Character> ->
        when (changes) {
            is UpdatedList -> {
                changes.insertions // indexes of inserted objects
                changes.insertionRanges // ranges of inserted objects
                changes.changes // indexes of modified objects
                changes.changeRanges // ranges of modified objects
                changes.deletions // indexes of deleted objects
                changes.deletionRanges // ranges of deleted objects
                changes.list // the full collection of objects
            }
            is DeletedList -> {
                // if the list was deleted
            }
        }
    }
}.

Unsubscribe a Change Listener

보고 있는 데이터의 업데이트에 대한 알림을 더 이상 수신하지 않으려면 변경 수신기에서 구독을 취소합니다.

 구독 취소하려면 둘러싸는 코루틴 취소 .

// query for the specific object you intend to listen to
val fellowshipOfTheRing = realm.query(Fellowship::class, "name == 'Fellowship of the Ring'").first().find()!!
val members = fellowshipOfTheRing.members
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
    val membersFlow = members.asFlow()
    membersFlow.collect { changes: ListChange<Character> ->
        // change listener stuff in here
    }
}
job.cancel() // cancel the coroutine containing the listener