Skip to content

Commit

Permalink
Working on strict concurrency
Browse files Browse the repository at this point in the history
  • Loading branch information
lickel committed Jun 24, 2024
1 parent b11f4c0 commit 7580df5
Show file tree
Hide file tree
Showing 12 changed files with 47 additions and 28 deletions.
8 changes: 5 additions & 3 deletions FetchRequests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@
BuildIndependentTargetsInParallel = YES;
CLASSPREFIX = "";
LastSwiftUpdateCheck = 1100;
LastUpgradeCheck = 1530;
LastUpgradeCheck = 1540;
ORGANIZATIONNAME = "Speramus Inc.";
TargetAttributes = {
471C506B22C6D0DB007F73E9 = {
Expand Down Expand Up @@ -1215,6 +1215,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
Expand Down Expand Up @@ -1283,7 +1284,7 @@
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = targeted;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "";
Expand All @@ -1300,6 +1301,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
Expand Down Expand Up @@ -1361,7 +1363,7 @@
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = targeted;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct AssociatedValueKey<FetchedObject: FetchableObject>: Hashable {
var keyPath: PartialKeyPath<FetchedObject>
}

class FetchableAssociatedValueReference<Entity: FetchableObject>: AssociatedValueReference {
class FetchableAssociatedValueReference<Entity: FetchableObject>: AssociatedValueReference, @unchecked Sendable {
private var observations: [Entity: [InvalidatableToken]] = [:]

fileprivate override func stopObservingValue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,16 @@ public extension CollapsibleSectionsFetchedResultsController {
// MARK: - Fetch Methods
public extension CollapsibleSectionsFetchedResultsController {
@MainActor
func performFetch(completion: @escaping @MainActor () -> Void = {}) {
func performFetch(
completion: @escaping @MainActor @Sendable () -> Void = {}
) {
fetchController.performFetch(completion: completion)
}

@MainActor
func resort(
using newSortDescriptors: [NSSortDescriptor],
completion: @escaping @MainActor () -> Void = {}
completion: @escaping @MainActor @Sendable () -> Void = {}
) {
fetchController.resort(using: newSortDescriptors, completion: completion)
}
Expand Down
29 changes: 17 additions & 12 deletions FetchRequests/Sources/Controller/FetchedResultsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import WatchKit

// MARK: - Delegate

public enum FetchedResultsChange<Location: Equatable>: Equatable {
public enum FetchedResultsChange<Location: Equatable & Sendable>: Equatable, Sendable {
case insert(location: Location)
case delete(location: Location)
case update(location: Location)
Expand Down Expand Up @@ -151,7 +151,7 @@ public enum FetchedResultsError: Error {

// MARK: - Sections

public struct FetchedResultsSection<FetchedObject: FetchableObject>: Equatable, Identifiable {
public struct FetchedResultsSection<FetchedObject: FetchableObject>: Equatable, Identifiable, Sendable {
public let name: String
public fileprivate(set) var objects: [FetchedObject]

Expand All @@ -171,7 +171,10 @@ public struct FetchedResultsSection<FetchedObject: FetchableObject>: Equatable,

// MARK: - FetchedResultsController

func performOnMainThread(async: Bool = true, handler: @escaping @MainActor () -> Void) {
func performOnMainThread(
async: Bool = true,
handler: @escaping @MainActor @Sendable () -> Void
) {
@MainActor(unsafe)
func unsafeHandler() {
handler()
Expand All @@ -188,7 +191,7 @@ func performOnMainThread(async: Bool = true, handler: @escaping @MainActor () ->
}
}

public class FetchedResultsController<FetchedObject: FetchableObject>: NSObject, FetchedResultsControllerProtocol {
public class FetchedResultsController<FetchedObject: FetchableObject>: NSObject, FetchedResultsControllerProtocol, @unchecked Sendable {
public typealias Delegate = FetchedResultsControllerDelegate<FetchedObject>

public typealias Section = FetchedResultsSection<FetchedObject>
Expand Down Expand Up @@ -338,7 +341,9 @@ public class FetchedResultsController<FetchedObject: FetchableObject>: NSObject,

public extension FetchedResultsController {
@MainActor
func performFetch(completion: @escaping @MainActor () -> Void) {
func performFetch(
completion: @escaping @MainActor @Sendable () -> Void
) {
startObservingNotificationsIfNeeded()

definition.request { [weak self] objects in
Expand All @@ -353,7 +358,7 @@ public extension FetchedResultsController {
@MainActor
func resort(
using newSortDescriptors: [NSSortDescriptor],
completion: @escaping @MainActor () -> Void
completion: @escaping @MainActor @Sendable () -> Void
) {
assert(Thread.isMainThread)

Expand Down Expand Up @@ -555,7 +560,7 @@ private extension FetchedResultsController {
updateFetchOrder: Bool = true,
emitChanges: Bool = true,
dropObjectsToInsert: Bool = true,
completion: @escaping @MainActor () -> Void
completion: @escaping @MainActor @Sendable () -> Void
) {
assert(Thread.isMainThread)

Expand All @@ -573,7 +578,7 @@ private extension FetchedResultsController {
updateFetchOrder: Bool = true,
emitChanges: Bool = true,
dropObjectsToInsert: Bool = true,
completion: @escaping @MainActor () -> Void
completion: @escaping @MainActor @Sendable () -> Void
) {
guard objects.count <= 100 || !Thread.isMainThread else {
// Bounce ourself off of the main queue
Expand Down Expand Up @@ -667,10 +672,10 @@ private extension FetchedResultsController {
}

@MainActor
func insert<C: Collection>(
func insert<C: Collection & Sendable>(
_ objects: C,
emitChanges: Bool = true,
completion: @escaping @MainActor () -> Void
completion: @escaping @MainActor @Sendable () -> Void
) where C.Element == FetchedObject {
// This is snapshotted because we're potentially about to be off the main thread
let fetchedObjectIDs = self.fetchedObjectIDs
Expand Down Expand Up @@ -705,11 +710,11 @@ private extension FetchedResultsController {
return sortedObjects
}

private func unsafeInsert<C: Collection>(
private func unsafeInsert<C: Collection & Sendable>(
_ objects: C,
fetchedObjectIDs: OrderedSet<FetchedObject.ID>,
emitChanges: Bool = true,
completion: @escaping @MainActor () -> Void
completion: @escaping @MainActor @Sendable () -> Void
) where C.Element == FetchedObject {
guard objects.count <= 100 || !Thread.isMainThread else {
// Bounce ourself off of the main queue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ public protocol FetchedResultsControllerProtocol<FetchedObject>: DoublyObservabl
var sortDescriptors: [NSSortDescriptor] { get }

@MainActor
func performFetch(completion: @escaping @MainActor () -> Void)
func performFetch(
completion: @escaping @MainActor @Sendable () -> Void
)
@MainActor
func resort(using newSortDescriptors: [NSSortDescriptor], completion: @escaping @MainActor () -> Void)
func resort(
using newSortDescriptors: [NSSortDescriptor],
completion: @escaping @MainActor @Sendable () -> Void
)
@MainActor
func reset()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,17 @@ public class PausableFetchedResultsController<FetchedObject: FetchableObject> {

extension PausableFetchedResultsController: FetchedResultsControllerProtocol {
@MainActor
public func performFetch(completion: @escaping @MainActor () -> Void) {
public func performFetch(
completion: @escaping @MainActor @Sendable () -> Void
) {
controller.performFetch(completion: completion)
}

@MainActor
public func resort(using newSortDescriptors: [NSSortDescriptor], completion: @escaping @MainActor () -> Void) {
public func resort(
using newSortDescriptors: [NSSortDescriptor],
completion: @escaping @MainActor @Sendable () -> Void
) {
controller.resort(using: newSortDescriptors, completion: completion)
}

Expand Down
2 changes: 1 addition & 1 deletion FetchRequests/Sources/FetchableObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public protocol RawDataRepresentable<RawData> {
}

/// A class of types that should be fetchable via FetchRequests
public protocol FetchableObjectProtocol: NSObjectProtocol, Identifiable, RawDataRepresentable {
public protocol FetchableObjectProtocol: NSObjectProtocol, Identifiable, RawDataRepresentable, Sendable where ID: Sendable {
/// Has this object been marked as deleted?
var isDeleted: Bool { get }

Expand Down

0 comments on commit 7580df5

Please sign in to comment.