diff --git a/App/Composition/SmiliePicker/SmilieSearchViewModel.swift b/App/Composition/SmiliePicker/SmilieSearchViewModel.swift index 26da62eda..70a48901c 100644 --- a/App/Composition/SmiliePicker/SmilieSearchViewModel.swift +++ b/App/Composition/SmiliePicker/SmilieSearchViewModel.swift @@ -197,12 +197,24 @@ final class SmilieSearchViewModel: ObservableObject { func updateLastUsedDate(for smilie: Smilie) { guard let context = dataStore.managedObjectContext else { return } - context.perform { - smilie.metadata.lastUsedDate = Date() - do { - try context.save() - } catch { - print("Error saving last used date: \(error)") + let smilieID = smilie.objectID + Task { + await context.perform { + guard let smilie = context.object(with: smilieID) as? Smilie, + let smilieText = smilie.text else { return } + + let fetchRequest = NSFetchRequest(entityName: "Smilie") + fetchRequest.predicate = NSPredicate(format: "text == %@", smilieText) + fetchRequest.fetchLimit = 1 + + do { + if let smilie = try context.fetch(fetchRequest).first { + smilie.metadata.lastUsedDate = Date() + try context.save() + } + } catch { + print("Error saving last used date: \(error)") + } } } } diff --git a/App/Refreshers/AnnouncementListRefresher.swift b/App/Refreshers/AnnouncementListRefresher.swift index 7eeeccded..c567e247a 100644 --- a/App/Refreshers/AnnouncementListRefresher.swift +++ b/App/Refreshers/AnnouncementListRefresher.swift @@ -3,7 +3,7 @@ // Copyright 2017 Awful Contributors. CC BY-NC-SA 3.0 US https://github.com/Awful/Awful.app import AwfulCore -import CoreData +@preconcurrency import CoreData import os import UIKit diff --git a/App/View Controllers/Posts/PostsPageViewController.swift b/App/View Controllers/Posts/PostsPageViewController.swift index 77720bd45..31e12e867 100644 --- a/App/View Controllers/Posts/PostsPageViewController.swift +++ b/App/View Controllers/Posts/PostsPageViewController.swift @@ -2,12 +2,12 @@ // // Copyright 2016 Awful Contributors. CC BY-NC-SA 3.0 US https://github.com/Awful/Awful.app -import AwfulCore +@preconcurrency import AwfulCore import AwfulModelTypes import AwfulSettings import AwfulTheming import Combine -import CoreData +@preconcurrency import CoreData import MobileCoreServices import MRProgress import os @@ -41,7 +41,9 @@ final class PostsPageViewController: ViewController { @FoilDefaultStorage(Settings.jumpToPostEndOnDoubleTap) private var jumpToPostEndOnDoubleTap private var jumpToPostIDAfterLoading: String? private var messageViewController: MessageComposeViewController? - private var networkOperation: Task<(posts: [Post], firstUnreadPost: Int?, advertisementHTML: String), Error>? + // Stored as Any because Task returns non-Sendable Core Data objects (Post: NSManagedObject). + // Swift 6 requires Task Success types to be Sendable. + private var networkOperation: Any? private var observers: [NSKeyValueObservation] = [] private lazy var oEmbedFetcher: OEmbedFetcher = .init() private(set) var page: ThreadPage? @@ -160,7 +162,7 @@ final class PostsPageViewController: ViewController { } deinit { - networkOperation?.cancel() + (networkOperation as? Task<(posts: [Post], firstUnreadPost: Int?, advertisementHTML: String), Error>)?.cancel() } var posts: [Post] = [] @@ -198,7 +200,7 @@ final class PostsPageViewController: ViewController { ) { flagRequest?.cancel() flagRequest = nil - networkOperation?.cancel() + (networkOperation as? Task<(posts: [Post], firstUnreadPost: Int?, advertisementHTML: String), Error>)?.cancel() networkOperation = nil // prevent white flash caused by webview being opaque during refreshes diff --git a/AwfulCore/Sources/AwfulCore/Model/AwfulManagedObject.swift b/AwfulCore/Sources/AwfulCore/Model/AwfulManagedObject.swift index 447acb7f6..0ee7c99a0 100644 --- a/AwfulCore/Sources/AwfulCore/Model/AwfulManagedObject.swift +++ b/AwfulCore/Sources/AwfulCore/Model/AwfulManagedObject.swift @@ -5,7 +5,7 @@ import CoreData /// A slightly more convenient NSManagedObject for entities with a custom class. -public class AwfulManagedObject: NSManagedObject { +public class AwfulManagedObject: NSManagedObject, @unchecked Sendable { public var objectKey: AwfulObjectKey { fatalError("subclass implementation please") } diff --git a/AwfulCore/Sources/AwfulCore/Model/Post.swift b/AwfulCore/Sources/AwfulCore/Model/Post.swift index da37f0d28..1bfaecb36 100644 --- a/AwfulCore/Sources/AwfulCore/Model/Post.swift +++ b/AwfulCore/Sources/AwfulCore/Model/Post.swift @@ -6,7 +6,7 @@ import CoreData /// A single reply to a thread. @objc(Post) -public class Post: AwfulManagedObject, Managed { +public class Post: AwfulManagedObject, Managed, @unchecked Sendable { public static var entityName: String { "Post" } /// Whether the logged-in user can edit the post. diff --git a/Config/Common.xcconfig b/Config/Common.xcconfig index 3aefd43a2..ede9fae37 100644 --- a/Config/Common.xcconfig +++ b/Config/Common.xcconfig @@ -75,6 +75,7 @@ RUN_CLANG_STATIC_ANALYZER = YES SDKROOT = iphoneos SWIFT_EMIT_LOC_STRINGS = YES +SWIFT_STRICT_CONCURRENCY = minimal SWIFT_TREAT_WARNINGS_AS_ERRORS = YES SWIFT_VERSION = 5.0