Skip to content

Commit

Permalink
Merge pull request #21232 from wordpress-mobile/fix/core-data-violati…
Browse files Browse the repository at this point in the history
…on-reminders

Fix Core Data threading violation when scheduling reminders
  • Loading branch information
kean authored Aug 8, 2023
2 parents e868c18 + 35a553a commit 5c34354
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 28 deletions.
2 changes: 1 addition & 1 deletion RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
23.1
-----

* [*] [internal] Fix Core Data multithreaded access exception in Blogging Reminders [#21232]

23.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ class BloggingRemindersScheduler {
///
private let pushNotificationAuthorizer: PushNotificationAuthorizer

private var coreDataStack: CoreDataStackSwift {
ContextManager.shared
}
private let coreDataStack: CoreDataStackSwift

/// The time of the day when blogging reminders will be received for the given blog
/// - Parameter blog: the given blog
Expand Down Expand Up @@ -238,11 +236,13 @@ class BloggingRemindersScheduler {
init(
store: BloggingRemindersStore,
notificationCenter: NotificationScheduler = UNUserNotificationCenter.current(),
pushNotificationAuthorizer: PushNotificationAuthorizer = InteractiveNotificationsManager.shared) {

self.store = store
self.notificationScheduler = notificationCenter
self.pushNotificationAuthorizer = pushNotificationAuthorizer
pushNotificationAuthorizer: PushNotificationAuthorizer = InteractiveNotificationsManager.shared,
coreDataStack: CoreDataStackSwift = ContextManager.shared
) {
self.store = store
self.notificationScheduler = notificationCenter
self.pushNotificationAuthorizer = pushNotificationAuthorizer
self.coreDataStack = coreDataStack
}

/// Default initializer. Allows overriding the blogging reminders store and the notification center for testing purposes.
Expand All @@ -254,11 +254,13 @@ class BloggingRemindersScheduler {
///
init(
notificationCenter: NotificationScheduler = UNUserNotificationCenter.current(),
pushNotificationAuthorizer: PushNotificationAuthorizer = InteractiveNotificationsManager.shared) throws {

pushNotificationAuthorizer: PushNotificationAuthorizer = InteractiveNotificationsManager.shared,
coreDataStack: CoreDataStackSwift = ContextManager.shared
) throws {
self.store = try Self.defaultStore()
self.notificationScheduler = notificationCenter
self.pushNotificationAuthorizer = pushNotificationAuthorizer
self.coreDataStack = coreDataStack
}

// MARK: - Scheduling
Expand All @@ -276,17 +278,21 @@ class BloggingRemindersScheduler {
return
}

let blogObjectID = blog.objectID
pushNotificationAuthorizer.requestAuthorization { [weak self] allowed in
guard let self = self else {
return
}

guard allowed else {
completion(.failure(Error.needsPermissionForPushNotifications))
return
DispatchQueue.main.async {
guard let self else { return }
guard allowed else {
completion(.failure(Error.needsPermissionForPushNotifications))
return
}
do {
let blog = try self.coreDataStack.mainContext.existingObject(with: blogObjectID) as! Blog
self.pushAuthorizationReceived(blog: blog, schedule: schedule, time: time, completion: completion)
} catch {
completion(.failure(error))
}
}

self.pushAuthorizationReceived(blog: blog, schedule: schedule, time: time, completion: completion)
}
}

Expand Down
22 changes: 14 additions & 8 deletions WordPress/WordPressTest/BloggingRemindersSchedulerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,15 @@ class BloggingRemindersSchedulerTests: XCTestCase {
let scheduler = BloggingRemindersScheduler(
store: store,
notificationCenter: notificationCenter,
pushNotificationAuthorizer: PushNotificationsAuthorizerMock())
pushNotificationAuthorizer: PushNotificationsAuthorizerMock(),
coreDataStack: contextManager
)

let expectation = self.expectation(description: "schedule-updated")
scheduler.schedule(schedule, for: blog) { _ in
expectation.fulfill()
}
wait(for: [expectation], timeout: 1)

XCTAssertEqual(scheduler.schedule(for: blog), schedule)
}
Expand Down Expand Up @@ -114,7 +119,9 @@ class BloggingRemindersSchedulerTests: XCTestCase {
let scheduler = BloggingRemindersScheduler(
store: store,
notificationCenter: notificationCenter,
pushNotificationAuthorizer: PushNotificationsAuthorizerMock())
pushNotificationAuthorizer: PushNotificationsAuthorizerMock(),
coreDataStack: contextManager
)

let storeHasRemindersExpectation = expectation(description: "The notifications are in the store")
let storeIsEmptyExpectation = expectation(description: "The notifications have been cleared from the store")
Expand All @@ -123,15 +130,14 @@ class BloggingRemindersSchedulerTests: XCTestCase {
if store.scheduledReminders(for: blog.objectID.uriRepresentation()) != .none {
storeHasRemindersExpectation.fulfill()
}
}

scheduler.schedule(.none, for: blog) { _ in
if store.scheduledReminders(for: blog.objectID.uriRepresentation()) == .none {
storeIsEmptyExpectation.fulfill()
scheduler.schedule(.none, for: blog) { _ in
if store.scheduledReminders(for: blog.objectID.uriRepresentation()) == .none {
storeIsEmptyExpectation.fulfill()
}
}
}

waitForExpectations(timeout: 0.1) { error in
waitForExpectations(timeout: 1) { error in
if let error = error {
XCTFail(error.localizedDescription)
}
Expand Down

0 comments on commit 5c34354

Please sign in to comment.