Skip to content

Commit

Permalink
Merge pull request #21008 from wordpress-mobile/perform-query-runtime…
Browse files Browse the repository at this point in the history
…-check

Add warnings about incorrect `performQuery` usages
  • Loading branch information
crazytonyli authored Jul 6, 2023
2 parents 5fb6a2c + 12554ea commit e280b09
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions WordPress/Classes/Utility/CoreDataHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ extension ContextManager.ContextManagerError: LocalizedError, CustomDebugStringC

extension CoreDataStack {
/// Perform a query using the `mainContext` and return the result.
///
/// - Warning: Do not return `NSManagedObject` instances from the closure.
func performQuery<T>(_ block: @escaping (NSManagedObjectContext) -> T) -> T {
mainContext.performAndWait { [mainContext] in
block(mainContext)
Expand Down Expand Up @@ -358,3 +360,69 @@ extension CoreDataStack {
try ContextManager.migrateDataModelsIfNecessary(storeURL: databaseLocation, objectModel: objectModel)
}
}

/// This extension declares many `performQuery` usages that may introduce Core Data concurrency issues.
///
/// The context object used by the `performQuery` function is opaque to the caller. The caller should not assume what
/// the context object is, nor the context queue type (the main queue or a background queue). That means the caller
/// does not have enough information to guarantee safe access to the returned `NSManagedObject` instances.
///
/// The closure passed to the `performQuery` function should use the context to query objects and return non- Core Data
/// types. Here is an example of how it should be used.
///
/// ```
/// // Wrong:
/// let account = coreDataStack.performQuery { context in
/// return Account.lookUp(in: context)
/// }
/// let name = account.username
///
/// // Right:
/// let name = coreDataStack.performQuery { context in
/// let account = Account.lookUp(in: context)
/// return account.username
/// }
/// ```
extension CoreDataStack {
@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T>(_ block: @escaping (NSManagedObjectContext) -> T) -> T where T: NSManagedObject {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}

@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T>(_ block: @escaping (NSManagedObjectContext) -> T?) -> T? where T: NSManagedObject {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}

@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T>(_ block: @escaping (NSManagedObjectContext) -> T) -> T where T: Sequence, T.Element: NSManagedObject {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}

@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T>(_ block: @escaping (NSManagedObjectContext) -> T?) -> T? where T: Sequence, T.Element: NSManagedObject {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}

@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T, E>(_ block: @escaping (NSManagedObjectContext) -> Result<T, E>) -> Result<T, E> where T: NSManagedObject, E: Error {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}

@available(*, deprecated, message: "Returning `NSManagedObject` instances may introduce Core Data concurrency issues.")
func performQuery<T, E>(_ block: @escaping (NSManagedObjectContext) -> Result<T, E>?) -> Result<T, E>? where T: NSManagedObject, E: Error {
mainContext.performAndWait { [mainContext] in
block(mainContext)
}
}
}

0 comments on commit e280b09

Please sign in to comment.