From 00a2b2e95dd350b7e7c9678cf75d7df6ed524546 Mon Sep 17 00:00:00 2001 From: kiancheong Date: Fri, 15 Feb 2019 10:02:02 +0800 Subject: [PATCH 1/3] Swift 4.2 Update --- Moody/Moody.xcodeproj/project.pbxproj | 12 +++++----- Moody/MoodyModel/MoodyMergePolicy.swift | 2 +- .../ApplicationActiveStateObserver.swift | 6 ++--- Moody/MoodySync/CloudKit+Extensions.swift | 23 ++++++++++--------- Moody/MoodySync/CloudKitRemote.swift | 22 ++++++++---------- Moody/MoodySync/ConsoleRemote.swift | 11 +++------ Moody/MoodySync/MoodDownloader.swift | 2 +- Moody/MoodySync/SyncContextOwner.swift | 2 +- .../DominantColor/ColorSpaceConversion.swift | 9 ++++---- SharedCode/Utilities.swift | 9 ++------ 10 files changed, 43 insertions(+), 55 deletions(-) diff --git a/Moody/Moody.xcodeproj/project.pbxproj b/Moody/Moody.xcodeproj/project.pbxproj index e590b0e..4e13a9a 100644 --- a/Moody/Moody.xcodeproj/project.pbxproj +++ b/Moody/Moody.xcodeproj/project.pbxproj @@ -1293,7 +1293,7 @@ buildSettings = { CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 58PSC9526Y; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -1303,7 +1303,7 @@ buildSettings = { CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 58PSC9526Y; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -1398,7 +1398,7 @@ buildSettings = { CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 58PSC9526Y; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -1408,7 +1408,7 @@ buildSettings = { CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 58PSC9526Y; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -1442,7 +1442,7 @@ DYLIB_CURRENT_VERSION = 1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -1457,7 +1457,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Moody/MoodyModel/MoodyMergePolicy.swift b/Moody/MoodyModel/MoodyMergePolicy.swift index 380e2c8..77b4c40 100644 --- a/Moody/MoodyModel/MoodyMergePolicy.swift +++ b/Moody/MoodyModel/MoodyMergePolicy.swift @@ -81,7 +81,7 @@ extension NSMergeConflict { extension Sequence where Iterator.Element == NSMergeConflict { func conflictedObjects(of cls: T.Type) -> [T] { let objects = map { $0.sourceObject } - return objects.flatMap { $0 as? T } + return objects.compactMap { $0 as? T } } func conflictsAndObjects(of cls: T.Type) -> [(NSMergeConflict, T)] { diff --git a/Moody/MoodySync/ApplicationActiveStateObserver.swift b/Moody/MoodySync/ApplicationActiveStateObserver.swift index 012ef90..4058ecc 100644 --- a/Moody/MoodySync/ApplicationActiveStateObserver.swift +++ b/Moody/MoodySync/ApplicationActiveStateObserver.swift @@ -17,7 +17,7 @@ protocol ObserverTokenStore : class { /// This is a helper protocol for the SyncCoordinator. /// /// It receives application active / background state changes and forwards them after switching onto the right queue. -protocol ApplicationActiveStateObserving : class, ObserverTokenStore { +protocol ApplicationActiveStateObserving : ObserverTokenStore { /// Runs the given block on the right queue and dispatch group. func perform(_ block: @escaping () -> ()) @@ -29,13 +29,13 @@ protocol ApplicationActiveStateObserving : class, ObserverTokenStore { extension ApplicationActiveStateObserving { func setupApplicationActiveNotifications() { - addObserverToken(NotificationCenter.default.addObserver(forName: .UIApplicationDidEnterBackground, object: nil, queue: nil) { [weak self] note in + addObserverToken(NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] note in guard let observer = self else { return } observer.perform { observer.applicationDidEnterBackground() } }) - addObserverToken(NotificationCenter.default.addObserver(forName: .UIApplicationDidBecomeActive, object: nil, queue: nil) { [weak self] note in + addObserverToken(NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] note in guard let observer = self else { return } observer.perform { observer.applicationDidBecomeActive() diff --git a/Moody/MoodySync/CloudKit+Extensions.swift b/Moody/MoodySync/CloudKit+Extensions.swift index 180dba7..b9ad438 100644 --- a/Moody/MoodySync/CloudKit+Extensions.swift +++ b/Moody/MoodySync/CloudKit+Extensions.swift @@ -12,16 +12,16 @@ import CloudKit enum CloudKitRecordChange { case created(CKRecord) case updated(CKRecord) - case deleted(CKRecordID) + case deleted(CKRecord.ID) } extension CKContainer { - func fetchAllPendingNotifications(changeToken: CKServerChangeToken?, processChanges: @escaping (_ changeReasons: [CKRecordID: CKQueryNotificationReason], _ error: NSError?, _ callback: @escaping (_ success: Bool) -> ()) -> ()) { + func fetchAllPendingNotifications(changeToken: CKServerChangeToken?, processChanges: @escaping (_ changeReasons: [CKRecord.ID: CKQueryNotification.Reason], _ error: NSError?, _ callback: @escaping (_ success: Bool) -> ()) -> ()) { let op = CKFetchNotificationChangesOperation(previousServerChangeToken: changeToken) - var changeReasons: [CKRecordID: CKQueryNotificationReason] = [:] - var notificationIDs: [CKNotificationID] = [] + var changeReasons: [CKRecord.ID: CKQueryNotification.Reason] = [:] + var notificationIDs: [CKNotification.ID] = [] op.notificationChangedBlock = { note in if let notificationID = note.notificationID { notificationIDs.append(notificationID) @@ -48,9 +48,9 @@ extension CKContainer { extension CKDatabase { - func fetchRecords(for changeReasons: [CKRecordID: CKQueryNotificationReason], completion: @escaping ([CloudKitRecordChange], NSError?) -> ()) { - var deletedIDs: [CKRecordID] = [] - var insertedOrUpdatedIDs: [CKRecordID] = [] + func fetchRecords(for changeReasons: [CKRecord.ID: CKQueryNotification.Reason], completion: @escaping ([CloudKitRecordChange], NSError?) -> ()) { + var deletedIDs: [CKRecord.ID] = [] + var insertedOrUpdatedIDs: [CKRecord.ID] = [] for (id, reason) in changeReasons { switch reason { case .recordDeleted: deletedIDs.append(id) @@ -96,15 +96,15 @@ extension CKQueryOperation { extension NSError { - func partiallyFailedRecords() -> [CKRecordID:NSError] { + func partiallyFailedRecords() -> [CKRecord.ID:NSError] { guard domain == CKErrorDomain else { return [:] } let errorCode = CKError.Code(rawValue: code) guard errorCode == .partialFailure else { return [:] } - return userInfo[CKPartialErrorsByItemIDKey] as? [CKRecordID:NSError] ?? [:] + return userInfo[CKPartialErrorsByItemIDKey] as? [CKRecord.ID:NSError] ?? [:] } - var partiallyFailedRecordIDsWithPermanentError: [CKRecordID] { - var result: [CKRecordID] = [] + var partiallyFailedRecordIDsWithPermanentError: [CKRecord.ID] { + var result: [CKRecord.ID] = [] for (remoteID, partialError) in partiallyFailedRecords() { if partialError.permanentCloudKitError { result.append(remoteID) @@ -151,6 +151,7 @@ extension NSError { case .managedAccountRestricted: return true case .participantMayNeedVerification: return true case .serverResponseLost: return false + case .assetNotAvailable: return true } } } diff --git a/Moody/MoodySync/CloudKitRemote.swift b/Moody/MoodySync/CloudKitRemote.swift index 948d220..9642708 100644 --- a/Moody/MoodySync/CloudKitRemote.swift +++ b/Moody/MoodySync/CloudKitRemote.swift @@ -17,9 +17,9 @@ final class CloudKitRemote: MoodyRemote { func setupMoodSubscription() { let subscriptionID = "MoodDownload" let predicate = NSPredicate(format: "TRUEPREDICATE") - let options: CKQuerySubscriptionOptions = [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion] + let options: CKQuerySubscription.Options = [.firesOnRecordCreation, .firesOnRecordUpdate, .firesOnRecordDeletion] let subscription = CKQuerySubscription(recordType: "Mood", predicate: predicate, subscriptionID: subscriptionID, options: options) - let info = CKNotificationInfo() + let info = CKSubscription.NotificationInfo() info.shouldSendContentAvailable = true subscription.notificationInfo = info let op = CKModifySubscriptionsOperation(subscriptionsToSave: [subscription], subscriptionIDsToDelete: []) @@ -36,7 +36,7 @@ final class CloudKitRemote: MoodyRemote { let op = CKQueryOperation(query: query) op.resultsLimit = maximumNumberOfMoods op.fetchAggregateResults(in: cloudKitContainer.publicCloudDatabase, previousResults: []) { records, _ in - completion(records.map { RemoteMood(record: $0) }.flatMap { $0 }) + completion(records.map { RemoteMood(record: $0) }.compactMap { $0 }) } } @@ -45,7 +45,7 @@ final class CloudKitRemote: MoodyRemote { guard error == nil else { return completion([], { _ in }) } // TODO We should handle this case with e.g. a clean refetch guard changeReasons.count > 0 else { return completion([], callback) } self.cloudKitContainer.publicCloudDatabase.fetchRecords(for: changeReasons) { changes, error in - completion(changes.map { RemoteRecordChange(moodChange: $0) }.flatMap { $0 }, callback) + completion(changes.map { RemoteRecordChange(moodChange: $0) }.compactMap { $0 }, callback) } } } @@ -55,7 +55,7 @@ final class CloudKitRemote: MoodyRemote { let op = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: nil) op.modifyRecordsCompletionBlock = { modifiedRecords, _, error in - let remoteMoods = modifiedRecords?.map { RemoteMood(record: $0) }.flatMap { $0 } ?? [] + let remoteMoods = modifiedRecords?.map { RemoteMood(record: $0) }.compactMap { $0 } ?? [] let remoteError = RemoteError(cloudKitError: error) completion(remoteMoods, remoteError) } @@ -63,9 +63,9 @@ final class CloudKitRemote: MoodyRemote { } func remove(_ moods: [Mood], completion: @escaping ([RemoteRecordID], RemoteError?) -> ()) { - let recordIDsToDelete = moods.map { (mood: Mood) -> CKRecordID in + let recordIDsToDelete = moods.map { (mood: Mood) -> CKRecord.ID in guard let name = mood.remoteIdentifier else { fatalError("Must have a remote ID") } - return CKRecordID(recordName: name) + return CKRecord.ID(recordName: name) } let op = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: recordIDsToDelete) op.modifyRecordsCompletionBlock = { _, deletedRecordIDs, error in @@ -124,12 +124,8 @@ extension RemoteMood { else { return nil } let isoCountry = ISO3166.Country(rawValue: Int16(countryCode)) ?? ISO3166.Country.unknown let location = record.object(forKey: "location") as? CLLocation - self.id = record.recordID.recordName - self.creatorID = creatorID - self.date = date - self.location = location - self.colors = colors - self.isoCountry = isoCountry + + self = RemoteMood(id: record.recordID.recordName, creatorID: creatorID, date: date, location: location, colors: colors, isoCountry: isoCountry) } } diff --git a/Moody/MoodySync/ConsoleRemote.swift b/Moody/MoodySync/ConsoleRemote.swift index e75b50b..d30fece 100644 --- a/Moody/MoodySync/ConsoleRemote.swift +++ b/Moody/MoodySync/ConsoleRemote.swift @@ -31,13 +31,13 @@ final class ConsoleRemote: MoodyRemote { func upload(_ moods: [Mood], completion: @escaping ([RemoteMood], RemoteError?) -> ()) { log("Uploading \(moods.count) moods") - let remoteMoods = moods.map { RemoteMood(mood: $0) }.flatMap { $0 } + let remoteMoods = moods.map { RemoteMood(mood: $0) }.compactMap { $0 } completion(remoteMoods, nil) } func remove(_ moods: [Mood], completion: @escaping ([RemoteRecordID], RemoteError?) -> ()) { log("Deleting \(moods.count) moods") - let ids = moods.map { $0.remoteIdentifier }.flatMap { $0 } + let ids = moods.map { $0.remoteIdentifier }.compactMap { $0 } completion(ids, nil) } @@ -51,12 +51,7 @@ final class ConsoleRemote: MoodyRemote { extension RemoteMood { fileprivate init?(mood: Mood) { - self.id = "__dummyId__" - self.creatorID = nil - self.date = mood.date - self.location = mood.location - self.colors = mood.colors - self.isoCountry = mood.country?.iso3166Code ?? .unknown + self = RemoteMood(id: "__dummyId__", creatorID: nil, date: mood.date, location: mood.location, colors: mood.colors, isoCountry: (mood.country?.iso3166Code ?? .unknown)) } } diff --git a/Moody/MoodySync/MoodDownloader.swift b/Moody/MoodySync/MoodDownloader.swift index 89087f6..cfec8c8 100644 --- a/Moody/MoodySync/MoodDownloader.swift +++ b/Moody/MoodySync/MoodDownloader.swift @@ -65,7 +65,7 @@ extension MoodDownloader { fileprivate func insert(_ remoteMoods: [RemoteMood], into context: NSManagedObjectContext) { let existingMoods = { () -> [RemoteRecordID: Mood] in - let ids = remoteMoods.map { $0.id }.flatMap { $0 } + let ids = remoteMoods.map { $0.id }.compactMap { $0 } let moods = Mood.fetch(in: context) { request in request.predicate = Mood.predicateForRemoteIdentifiers(ids) request.returnsObjectsAsFaults = false diff --git a/Moody/MoodySync/SyncContextOwner.swift b/Moody/MoodySync/SyncContextOwner.swift index bea6d7f..cc5b32b 100644 --- a/Moody/MoodySync/SyncContextOwner.swift +++ b/Moody/MoodySync/SyncContextOwner.swift @@ -15,7 +15,7 @@ import CoreDataHelpers /// /// This protocol merges changes from the view context into the sync context and vice versa. /// It calls its `process(changedLocalObjects:)` methods when objects have changed. -protocol ContextOwner: class, ObserverTokenStore { +protocol ContextOwner: ObserverTokenStore { /// The view managed object context. var viewContext: NSManagedObjectContext { get } /// The managed object context that is used to perform synchronization with the backend. diff --git a/Moody/lib/DominantColor/ColorSpaceConversion.swift b/Moody/lib/DominantColor/ColorSpaceConversion.swift index 8262b16..9e2f2aa 100644 --- a/Moody/lib/DominantColor/ColorSpaceConversion.swift +++ b/Moody/lib/DominantColor/ColorSpaceConversion.swift @@ -82,10 +82,11 @@ extension ARGBPixel_t { -0.091169, 0.25243, 0.015708, 0.0009209, -0.0025498, 0.1786, ] - self.a = UInt8.max - self.r = UInt8(max(min(xyz.x * matrix[0] + xyz.y * matrix[1] + xyz.z * matrix[2], Float(UInt8.max)), 0)) - self.g = UInt8(max(min(xyz.x * matrix[3] + xyz.y * matrix[4] + xyz.z * matrix[5], Float(UInt8.max)), 0)) - self.b = UInt8(max(min(xyz.x * matrix[6] + xyz.y * matrix[7] + xyz.z * matrix[8], Float(UInt8.max)), 0)) + let a = UInt8.max + let r = UInt8(max(min(xyz.x * matrix[0] + xyz.y * matrix[1] + xyz.z * matrix[2], Float(UInt8.max)), 0)) + let g = UInt8(max(min(xyz.x * matrix[3] + xyz.y * matrix[4] + xyz.z * matrix[5], Float(UInt8.max)), 0)) + let b = UInt8(max(min(xyz.x * matrix[6] + xyz.y * matrix[7] + xyz.z * matrix[8], Float(UInt8.max)), 0)) + self = ARGBPixel_t(a: a, r: r, g: g, b: b) } init(lab: LABPixel) { let xyz = XYZ(lab: lab) diff --git a/SharedCode/Utilities.swift b/SharedCode/Utilities.swift index 2ebae9b..080dec2 100644 --- a/SharedCode/Utilities.swift +++ b/SharedCode/Utilities.swift @@ -79,13 +79,8 @@ extension URL { extension String { public func removingCharacters(in set: CharacterSet) -> String { - var chars = characters - for idx in chars.indices.reversed() { - if set.contains(String(chars[idx]).unicodeScalars.first!) { - chars.remove(at: idx) - } - } - return String(chars) + let filtered = unicodeScalars.lazy.filter { !set.contains($0) } + return String(String.UnicodeScalarView(filtered)) } } From f2a00043e89b9f971a0683ae37b1e857452a1998 Mon Sep 17 00:00:00 2001 From: kiancheong Date: Fri, 15 Feb 2019 10:05:39 +0800 Subject: [PATCH 2/3] add .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..125ee7a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.xcuserdatad From 1e4c5cad88e3dcaf9d7315f9077041f4ed56ccad Mon Sep 17 00:00:00 2001 From: kiancheong Date: Wed, 1 May 2019 18:29:01 +0800 Subject: [PATCH 3/3] Swift 5 update --- SharedCode/ManagedObjectObserver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedCode/ManagedObjectObserver.swift b/SharedCode/ManagedObjectObserver.swift index bcc8f77..a6cfe07 100644 --- a/SharedCode/ManagedObjectObserver.swift +++ b/SharedCode/ManagedObjectObserver.swift @@ -27,7 +27,7 @@ public final class ManagedObjectObserver { } deinit { - NotificationCenter.default.removeObserver(token) + NotificationCenter.default.removeObserver(token as Any) }