Skip to content

Commit b5a4e72

Browse files
authored
Fix most common CoreData errors (#471)
Addresses the most common CoreData related crashes I have observed. This includes * Using contexts across threads * Keeping versions of goals before writing, leading to conflicts * Fix cascading deletion Testing: Run app on device
1 parent 86eba04 commit b5a4e72

File tree

3 files changed

+11
-11
lines changed

3 files changed

+11
-11
lines changed

BeeKit/Managers/GoalManager.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ public actor GoalManager {
7171
let responseObject = try await requestManager.get(url: "/api/v1/users/\(currentUserManager.username!)/goals/\(goal.slug)?datapoints_count=5", parameters: nil)
7272
let goalJSON = JSON(responseObject!)
7373

74+
// The goal may have changed during the network operation, reload latest version
75+
context.refresh(goal, mergeChanges: false)
7476
goal.updateToMatch(json: goalJSON)
75-
try context.save()
7677

78+
try context.save()
7779
await performPostGoalUpdateBookkeeping()
7880
}
7981

@@ -156,7 +158,6 @@ public actor GoalManager {
156158
try await withThrowingTaskGroup(of: Void.self) { group in
157159
for goal in queuedGoals {
158160
group.addTask {
159-
// TODO: We don't really need to reload the goal in a new context here
160161
try await self.refreshGoal(goal.objectID)
161162
}
162163
}

BeeKit/Managers/HealthStoreManager.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,11 @@ public class HealthStoreManager {
108108

109109
try await withThrowingTaskGroup(of: Void.self) { group in
110110
for goal in goalsWithHealthData {
111+
let goalID = goal.objectID
111112
group.addTask {
112-
try await self.updateWithRecentData(goal: goal, days: days)
113+
// This is a new thread, so we are not allowed to use the goal object from CoreData
114+
// TODO: This will generate lots of unneccesary reloads
115+
try await self.updateWithRecentData(goalID: goalID, days: days)
113116
}
114117
}
115118
try await group.waitForAll()
@@ -182,13 +185,8 @@ public class HealthStoreManager {
182185
return
183186
}
184187

185-
try await withThrowingTaskGroup(of: Void.self) { group in
186-
for goal in goalsForMetric {
187-
group.addTask {
188-
try await self.updateWithRecentData(goal: goal, days: HealthStoreManager.daysToUpdateOnChangeNotification)
189-
}
190-
}
191-
try await group.waitForAll()
188+
for goal in goalsForMetric {
189+
try await self.updateWithRecentData(goal: goal, days: HealthStoreManager.daysToUpdateOnChangeNotification)
192190
}
193191
} catch {
194192
logger.error("Error updating goals for metric change: \(error, privacy: .public)")

BeeKit/Model/BeeminderModel.xcdatamodeld/BeeminderModel.xcdatamodel/contents

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<attribute name="requestid" optional="YES" attributeType="String"/>
88
<attribute name="updatedAt" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
99
<attribute name="value" attributeType="Decimal" defaultValueString="0.0"/>
10-
<relationship name="goal" maxCount="1" deletionRule="Nullify" destinationEntity="Goal"/>
10+
<relationship name="goal" maxCount="1" deletionRule="Nullify" destinationEntity="Goal" inverseName="data" inverseEntity="Goal"/>
1111
</entity>
1212
<entity name="Goal" representedClassName="Goal" syncable="YES">
1313
<attribute name="alertStart" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
@@ -33,6 +33,7 @@
3333
<attribute name="useDefaults" attributeType="Boolean" usesScalarValueType="YES"/>
3434
<attribute name="won" attributeType="Boolean" usesScalarValueType="YES"/>
3535
<attribute name="yAxis" attributeType="String"/>
36+
<relationship name="data" toMany="YES" deletionRule="Cascade" destinationEntity="DataPoint" inverseName="goal" inverseEntity="DataPoint"/>
3637
<relationship name="owner" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="User" inverseName="goals" inverseEntity="User"/>
3738
<relationship name="recentData" toMany="YES" deletionRule="Nullify" destinationEntity="DataPoint"/>
3839
</entity>

0 commit comments

Comments
 (0)