Skip to content

Commit f31fca6

Browse files
committed
Fix the issue that the list element is not in local database when recovering the value of parent’s list property
1 parent c1a7ae0 commit f31fca6

5 files changed

+98
-32
lines changed

IceCream/Classes/CKRecordRecoverable.swift

+21-29
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ public protocol CKRecordRecoverable {
1313
}
1414

1515
extension CKRecordRecoverable where Self: Object {
16-
static func parseFromRecord<U: Object, V: Object, W: Object>(record: CKRecord, realm: Realm, notificationToken: NotificationToken?, uListType: U.Type, vListType: V.Type, wListType: W.Type) -> Self? {
16+
static func parseFromRecord<U: Object, V: Object, W: Object>(
17+
record: CKRecord,
18+
realm: Realm,
19+
notificationToken: NotificationToken?,
20+
pendingUTypeRelationshipsWorker: PendingRelationshipsWorker<U>,
21+
pendingVTypeRelationshipsWorker: PendingRelationshipsWorker<V>,
22+
pendingWTypeRelationshipsWorker: PendingRelationshipsWorker<W>
23+
) -> Self? {
1724
let o = Self()
1825
for prop in o.objectSchema.properties {
1926
var recordValue: Any?
@@ -57,66 +64,51 @@ extension CKRecordRecoverable where Self: Object {
5764
recordValue = list
5865
case .object:
5966
guard let value = record.value(forKey: prop.name) as? [CKRecord.Reference] else { break }
60-
// 这里应该根据 List 去创建一个 unmanaged 版本的 List,而不是使用 dynamicList 获取 managed 版本的 list
61-
// 另外需要考虑的就是类型问题
67+
6268
let uList = List<U>()
6369
let vList = List<V>()
6470
let wList = List<W>()
6571

66-
// guard let list = o.value(forKey: prop.name) as? List<Object> else { break }
67-
// let list = o.dynamicList(prop.name)
6872
for reference in value {
6973
if let objectClassName = prop.objectClassName,
7074
let schema = realm.schema.objectSchema.first(where: { $0.className == objectClassName }),
7175
let primaryKeyValue = primaryKeyForRecordID(recordID: reference.recordID, schema: schema) {
72-
73-
// 其实在这里一个数组里所有的 className 都只会是一种
74-
75-
if schema.className == uListType.className() {
76-
if let existObject = realm.object(ofType: uListType, forPrimaryKey: primaryKeyValue) {
76+
if schema.className == U.className() {
77+
if let existObject = realm.object(ofType: U.self, forPrimaryKey: primaryKeyValue) {
7778
uList.append(existObject)
7879
} else {
79-
try! realm.write {
80-
let object = realm.create(uListType)
81-
uList.append(object)
82-
}
80+
pendingUTypeRelationshipsWorker.addToPendingListElement(propertyName: prop.name, primaryKeyValue: primaryKeyValue)
8381
}
8482
}
8583

86-
if schema.className == vListType.className() {
87-
if let existObject = realm.object(ofType: vListType, forPrimaryKey: primaryKeyValue) {
84+
if schema.className == V.className() {
85+
if let existObject = realm.object(ofType: V.self, forPrimaryKey: primaryKeyValue) {
8886
vList.append(existObject)
8987
} else {
90-
try! realm.write {
91-
let object = realm.create(vListType)
92-
vList.append(object)
93-
}
88+
pendingVTypeRelationshipsWorker.addToPendingListElement(propertyName: prop.name, primaryKeyValue: primaryKeyValue)
9489
}
9590
}
9691

97-
if schema.className == wListType.className() {
98-
if let existObject = realm.object(ofType: wListType, forPrimaryKey: primaryKeyValue) {
92+
if schema.className == W.className() {
93+
if let existObject = realm.object(ofType: W.self, forPrimaryKey: primaryKeyValue) {
9994
wList.append(existObject)
10095
} else {
101-
try! realm.write {
102-
let object = realm.create(wListType)
103-
wList.append(object)
104-
}
96+
pendingWTypeRelationshipsWorker.addToPendingListElement(propertyName: prop.name, primaryKeyValue: primaryKeyValue)
10597
}
10698
}
10799

108100
}
109101
}
110102

111-
if prop.objectClassName == uListType.className() {
103+
if prop.objectClassName == U.className() {
112104
recordValue = uList
113105
}
114106

115-
if prop.objectClassName == vListType.className() {
107+
if prop.objectClassName == V.className() {
116108
recordValue = vList
117109
}
118110

119-
if prop.objectClassName == wListType.className() {
111+
if prop.objectClassName == W.className() {
120112
recordValue = wList
121113
}
122114

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Soledad on 2021/2/7.
6+
//
7+
8+
import Foundation
9+
import RealmSwift
10+
11+
final class PendingRelationshipsWorker<ListElement: Object> {
12+
13+
private let listElementType: ListElement.Type
14+
15+
var realm: Realm?
16+
var owner: Object?
17+
18+
private var pendingListElementPrimaryKeyValue: [String: Any] = [:]
19+
20+
init(listElementType: ListElement.Type) {
21+
self.listElementType = listElementType
22+
}
23+
24+
func addToPendingListElement(propertyName: String, primaryKeyValue: Any) {
25+
pendingListElementPrimaryKeyValue[propertyName] = primaryKeyValue
26+
}
27+
28+
func resolvePendingListElements() {
29+
guard let owner = owner, let realm = realm, pendingListElementPrimaryKeyValue.count > 0 else { return }
30+
BackgroundWorker.shared.start {
31+
for (propName, primaryKeyValue) in self.pendingListElementPrimaryKeyValue {
32+
guard let list = owner.value(forKey: propName) as? List<ListElement> else { return }
33+
if let existListElementObject = realm.object(ofType: self.listElementType, forPrimaryKey: primaryKeyValue) {
34+
try! realm.write {
35+
list.append(existListElementObject)
36+
}
37+
self.pendingListElementPrimaryKeyValue[propName] = nil
38+
}
39+
}
40+
}
41+
}
42+
43+
}

IceCream/Classes/PrivateDatabaseManager.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,11 @@ final class PrivateDatabaseManager: DatabaseManager {
183183
}
184184
}
185185

186-
changesOp.fetchRecordZoneChangesCompletionBlock = { error in
186+
changesOp.fetchRecordZoneChangesCompletionBlock = { [weak self] error in
187+
guard let self = self else { return }
188+
self.syncObjects.forEach {
189+
$0.resolvePendingRelationships()
190+
}
187191
callback?(error)
188192
}
189193

IceCream/Classes/SyncObject.swift

+27-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ public final class SyncObject<T, U, V, W> where T: Object & CKRecordConvertible
2222
private var notificationToken: NotificationToken?
2323

2424
public var pipeToEngine: ((_ recordsToStore: [CKRecord], _ recordIDsToDelete: [CKRecord.ID]) -> ())?
25-
// let listNameTypePairStore = ListNameTypePairStore(className: T.className())
2625

2726
public let realmConfiguration: Realm.Configuration
2827

28+
private let pendingUTypeRelationshipsWorker = PendingRelationshipsWorker(listElementType: U.self)
29+
private let pendingVTypeRelationshipsWorker = PendingRelationshipsWorker(listElementType: V.self)
30+
private let pendingWTypeRelationshipsWorker = PendingRelationshipsWorker(listElementType: W.self)
31+
2932
public init(
3033
realmConfiguration: Realm.Configuration = .defaultConfiguration,
3134
type: T.Type,
@@ -80,11 +83,27 @@ extension SyncObject: Syncable {
8083
public func add(record: CKRecord) {
8184
BackgroundWorker.shared.start {
8285
let realm = try! Realm(configuration: self.realmConfiguration)
83-
guard let object = T.parseFromRecord(record: record, realm: realm, notificationToken: self.notificationToken, uListType: U.self, vListType: V.self, wListType: W.self) else {
86+
guard let object = T.parseFromRecord(
87+
record: record,
88+
realm: realm,
89+
notificationToken: self.notificationToken,
90+
pendingUTypeRelationshipsWorker: self.pendingUTypeRelationshipsWorker,
91+
pendingVTypeRelationshipsWorker: self.pendingVTypeRelationshipsWorker,
92+
pendingWTypeRelationshipsWorker: self.pendingWTypeRelationshipsWorker
93+
) else {
8494
print("There is something wrong with the converson from cloud record to local object")
8595
return
8696
}
8797

98+
self.pendingUTypeRelationshipsWorker.owner = object
99+
self.pendingUTypeRelationshipsWorker.realm = realm
100+
101+
self.pendingVTypeRelationshipsWorker.owner = object
102+
self.pendingVTypeRelationshipsWorker.realm = realm
103+
104+
self.pendingWTypeRelationshipsWorker.owner = object
105+
self.pendingWTypeRelationshipsWorker.realm = realm
106+
88107
/// If your model class includes a primary key, you can have Realm intelligently update or add objects based off of their primary key values using Realm().add(_:update:).
89108
/// https://realm.io/docs/swift/latest/#objects-with-primary-keys
90109
realm.beginWrite()
@@ -138,6 +157,12 @@ extension SyncObject: Syncable {
138157
}
139158
}
140159

160+
public func resolvePendingRelationships() {
161+
pendingUTypeRelationshipsWorker.resolvePendingListElements()
162+
pendingVTypeRelationshipsWorker.resolvePendingListElements()
163+
pendingWTypeRelationshipsWorker.resolvePendingListElements()
164+
}
165+
141166
public func cleanUp() {
142167
BackgroundWorker.shared.start {
143168
let realm = try! Realm(configuration: self.realmConfiguration)

IceCream/Classes/Syncable.swift

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public protocol Syncable: class {
2727
func add(record: CKRecord)
2828
func delete(recordID: CKRecord.ID)
2929

30+
func resolvePendingRelationships()
31+
3032
/// CloudKit related
3133
func pushLocalObjectsToCloudKit()
3234

0 commit comments

Comments
 (0)