diff --git a/Account/Sources/Account/AccountManager.swift b/Account/Sources/Account/AccountManager.swift index b71e981be..3f517cf6d 100644 --- a/Account/Sources/Account/AccountManager.swift +++ b/Account/Sources/Account/AccountManager.swift @@ -8,6 +8,7 @@ import Foundation import RSCore +import RSWeb import Articles import ArticlesDatabase @@ -228,6 +229,8 @@ public final class AccountManager: UnreadCountProvider { } public func refreshAll(errorHandler: @escaping (Error) -> Void, completion: (() -> Void)? = nil) { + guard let reachability = try? Reachability(hostname: "apple.com"), reachability.connection != .unavailable else { return } + let group = DispatchGroup() activeAccounts.forEach { account in @@ -249,6 +252,8 @@ public final class AccountManager: UnreadCountProvider { } public func refreshAll(completion: (() -> Void)? = nil) { + guard let reachability = try? Reachability(hostname: "apple.com"), reachability.connection != .unavailable else { return } + var syncErrors = [AccountSyncError]() let group = DispatchGroup() diff --git a/Account/Sources/Account/CloudKit/CloudKitZone.swift b/Account/Sources/Account/CloudKit/CloudKitZone.swift index 099a7ae46..f70bdb391 100644 --- a/Account/Sources/Account/CloudKit/CloudKitZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitZone.swift @@ -33,6 +33,7 @@ typealias CloudKitRecordKey = (recordType: CKRecord.RecordType, recordID: CKReco protocol CloudKitZone: class { static var zoneID: CKRecordZone.ID { get } + static var qualityOfService: QualityOfService { get } var log: OSLog { get } @@ -56,6 +57,17 @@ protocol CloudKitZone: class { extension CloudKitZone { + // My observation has been that QoS is treated differently for CloudKit operations on macOS vs iOS. + // .userInitiated is too aggressive on iOS and can lead the UI slowing down and appearing to block. + // .default (or lower) on macOS will sometimes hang for extended periods of time and appear to hang. + static var qualityOfService: QualityOfService { + #if os(macOS) + return .userInitiated + #else + return .default + #endif + } + /// Reset the change token used to determine what point in time we are doing changes fetches func resetChangeToken() { changeToken = nil @@ -239,7 +251,7 @@ extension CloudKitZone { let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: [CKRecord.ID]()) op.savePolicy = .ifServerRecordUnchanged op.isAtomic = false - op.qualityOfService = .default + op.qualityOfService = Self.qualityOfService op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in @@ -352,7 +364,7 @@ extension CloudKitZone { var records = [CKRecord]() let op = CKQueryOperation(query: ckQuery) - op.qualityOfService = .default + op.qualityOfService = Self.qualityOfService op.recordFetchedBlock = { record in records.append(record) } @@ -389,7 +401,7 @@ extension CloudKitZone { var records = [CKRecord]() let op = CKQueryOperation(cursor: cursor) - op.qualityOfService = .default + op.qualityOfService = Self.qualityOfService op.recordFetchedBlock = { record in records.append(record) } @@ -471,7 +483,7 @@ extension CloudKitZone { let op = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: recordIDsToDelete) op.savePolicy = .changedKeys op.isAtomic = true - op.qualityOfService = .default + op.qualityOfService = Self.qualityOfService op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in @@ -568,7 +580,7 @@ extension CloudKitZone { zoneConfig.previousServerChangeToken = changeToken let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [Self.zoneID], configurationsByRecordZoneID: [Self.zoneID: zoneConfig]) op.fetchAllChanges = true - op.qualityOfService = .default + op.qualityOfService = Self.qualityOfService op.recordZoneChangeTokensUpdatedBlock = { zoneID, token, _ in savedChangeToken = token diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index 7cd4f8e12..79cbef912 100644 --- a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,68 +1,68 @@ { "images" : [ { - "size" : "16x16", + "filename" : "Icon-MacOS-16x16@1x.png.png", "idiom" : "mac", - "filename" : "icon_16x16.png", - "scale" : "1x" + "scale" : "1x", + "size" : "16x16" }, { - "size" : "16x16", + "filename" : "Icon-MacOS-16x16@2x.png.png", "idiom" : "mac", - "filename" : "icon_16x16@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "16x16" }, { - "size" : "32x32", + "filename" : "Icon-MacOS-32x32@1x.png.png", "idiom" : "mac", - "filename" : "icon_32x32.png", - "scale" : "1x" + "scale" : "1x", + "size" : "32x32" }, { - "size" : "32x32", + "filename" : "Icon-MacOS-32x32@2x.png.png", "idiom" : "mac", - "filename" : "icon_32x32@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "32x32" }, { - "size" : "128x128", + "filename" : "Icon-MacOS-128x128@1x.png.png", "idiom" : "mac", - "filename" : "icon_128x128.png", - "scale" : "1x" + "scale" : "1x", + "size" : "128x128" }, { - "size" : "128x128", + "filename" : "Icon-MacOS-128x128@2x.png.png", "idiom" : "mac", - "filename" : "icon_128x128@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "128x128" }, { - "size" : "256x256", + "filename" : "Icon-MacOS-256x256@1x.png.png", "idiom" : "mac", - "filename" : "icon_256x256.png", - "scale" : "1x" + "scale" : "1x", + "size" : "256x256" }, { - "size" : "256x256", + "filename" : "Icon-MacOS-256x256@2x.png.png", "idiom" : "mac", - "filename" : "icon_256x256@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "256x256" }, { - "size" : "512x512", + "filename" : "Icon-MacOS-512x512@1x.png.png", "idiom" : "mac", - "filename" : "icon_512x512.png", - "scale" : "1x" + "scale" : "1x", + "size" : "512x512" }, { - "size" : "512x512", + "filename" : "Icon-MacOS-512x512@2x.png.png", "idiom" : "mac", - "filename" : "icon_512x512@2x.png", - "scale" : "2x" + "scale" : "2x", + "size" : "512x512" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png new file mode 100644 index 000000000..e8c6573fd Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png new file mode 100644 index 000000000..176ad77e6 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png new file mode 100644 index 000000000..4477733ee Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png new file mode 100644 index 000000000..5a536a436 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png new file mode 100644 index 000000000..176ad77e6 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png new file mode 100644 index 000000000..943c3a89e Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png new file mode 100644 index 000000000..084985da6 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png new file mode 100644 index 000000000..0c1d6e3b4 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png new file mode 100644 index 000000000..943c3a89e Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png new file mode 100644 index 000000000..61f85ee9d Binary files /dev/null and b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png deleted file mode 100644 index 9b8e86460..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png deleted file mode 100644 index 9a57c5df3..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png deleted file mode 100644 index c8fc3ef69..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png deleted file mode 100644 index d93b02e85..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png deleted file mode 100644 index 9a57c5df3..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png deleted file mode 100644 index 9a0005029..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png deleted file mode 100644 index d93b02e85..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png deleted file mode 100644 index c5debdda8..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png deleted file mode 100644 index 9a0005029..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png deleted file mode 100644 index eb5fe64e1..000000000 Binary files a/Mac/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json index c85424576..8ba7ca9ea 100644 --- a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -109,61 +109,61 @@ "size" : "1024x1024" }, { - "filename" : "icon_16x16.png", + "filename" : "Icon-MacOS-16x16@1x.png.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { - "filename" : "icon_32x32.png", + "filename" : "Icon-MacOS-16x16@2x.png.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { - "filename" : "icon_32x32-1.png", + "filename" : "Icon-MacOS-32x32@1x.png.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { - "filename" : "icon_32x32@2x.png", + "filename" : "Icon-MacOS-32x32@2x.png.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { - "filename" : "icon_128x128.png", + "filename" : "Icon-MacOS-128x128@1x.png.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { - "filename" : "icon_128x128@2x.png", + "filename" : "Icon-MacOS-128x128@2x.png.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { - "filename" : "icon_256x256.png", + "filename" : "Icon-MacOS-256x256@1x.png.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { - "filename" : "icon_256x256@2x-1.png", + "filename" : "Icon-MacOS-256x256@2x.png.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { - "filename" : "icon_512x512.png", + "filename" : "Icon-MacOS-512x512@1x.png.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "icon_512x512@2x.png", + "filename" : "Icon-MacOS-512x512@2x.png.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png new file mode 100644 index 000000000..e8c6573fd Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@1x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png new file mode 100644 index 000000000..176ad77e6 Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-128x128@2x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png new file mode 100644 index 000000000..4477733ee Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@1x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png new file mode 100644 index 000000000..5a536a436 Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-16x16@2x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png new file mode 100644 index 000000000..176ad77e6 Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@1x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png new file mode 100644 index 000000000..943c3a89e Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-256x256@2x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png new file mode 100644 index 000000000..084985da6 Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@1x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png new file mode 100644 index 000000000..0c1d6e3b4 Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-32x32@2x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png new file mode 100644 index 000000000..943c3a89e Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@1x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png new file mode 100644 index 000000000..61f85ee9d Binary files /dev/null and b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/Icon-MacOS-512x512@2x.png.png differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128.png deleted file mode 100644 index 9b8e86460..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png deleted file mode 100644 index 9a57c5df3..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_16x16.png deleted file mode 100644 index c8fc3ef69..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_16x16.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256.png deleted file mode 100644 index 9a57c5df3..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x-1.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x-1.png deleted file mode 100644 index 9a0005029..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x-1.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32-1.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32-1.png deleted file mode 100644 index d93b02e85..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32-1.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32.png deleted file mode 100644 index d93b02e85..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png deleted file mode 100644 index c5debdda8..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512.png deleted file mode 100644 index 9a0005029..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512.png and /dev/null differ diff --git a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png deleted file mode 100644 index eb5fe64e1..000000000 Binary files a/Multiplatform/Shared/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png and /dev/null differ diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index ec073f240..b70495a7a 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -155,8 +155,6 @@ 513146B3235A81A400387FDC /* AddWebFeedIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513146B1235A81A400387FDC /* AddWebFeedIntentHandler.swift */; }; 51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; }; 51314705235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; }; - 513228FB233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; }; - 513228FC233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; }; 51333D1624685D2E00EB5C91 /* AddRedditFeedWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */; }; 51333D1724685D2E00EB5C91 /* AddRedditFeedWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */; }; 51333D3B2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51333D392468615D00EB5C91 /* AddRedditFeedSheet.xib */; }; @@ -584,8 +582,6 @@ 51E4993424A867E700B667CB /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; }; 51E4993524A867E800B667CB /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */; }; 51E4993624A867E800B667CB /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; }; - 51E4993724A8680E00B667CB /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; }; - 51E4993824A8680E00B667CB /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; }; 51E4993A24A8708800B667CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4993924A8708800B667CB /* AppDelegate.swift */; }; 51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4993B24A8709900B667CB /* AppDelegate.swift */; }; 51E4993D24A870F800B667CB /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; }; @@ -725,7 +721,6 @@ 65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; }; 65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A975D1ED9EB72007D329B /* MainWindowController.swift */; }; 65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; }; - 65ED3FC7235DEF6C0081F399 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; }; 65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */; }; 65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; }; 65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */; }; @@ -1526,7 +1521,6 @@ 513146B1235A81A400387FDC /* AddWebFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedIntentHandler.swift; sourceTree = ""; }; 51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; }; 51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = ""; }; - 513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; 51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRedditFeedWindowController.swift; sourceTree = ""; }; 51333D3A2468615D00EB5C91 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/AddRedditFeedSheet.xib; sourceTree = SOURCE_ROOT; }; 51392D1A24AC19A000BE0D35 /* SidebarExpandedContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarExpandedContainers.swift; sourceTree = ""; }; @@ -2387,14 +2381,6 @@ path = IntentsExtension; sourceTree = ""; }; - 513228F1233037620033D4ED /* Network */ = { - isa = PBXGroup; - children = ( - 513228F2233037620033D4ED /* Reachability.swift */, - ); - path = Network; - sourceTree = ""; - }; 513C5CE7232571C2003D4054 /* ShareExtension */ = { isa = PBXGroup; children = ( @@ -3309,7 +3295,6 @@ 512E08DD22687FA000BDCFDD /* Tree */, 849A97561ED9EB0D007D329B /* Extensions */, 510C43F5243D0325009F70C3 /* ExtensionPoints */, - 513228F1233037620033D4ED /* Network */, 511D43CE231FA51100FB1562 /* Resources */, ); path = Shared; @@ -4554,7 +4539,6 @@ 5177476724B3BE3400EB0F74 /* SettingsAboutModel.swift in Sources */, 65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */, 51E4990A24A808C500B667CB /* FeaturedImageDownloader.swift in Sources */, - 51E4993824A8680E00B667CB /* Reachability.swift in Sources */, 51E4993224A8676400B667CB /* FetchRequestQueue.swift in Sources */, 51E4991724A8090400B667CB /* ArticleUtilities.swift in Sources */, 51E4991B24A8091000B667CB /* IconImage.swift in Sources */, @@ -4672,7 +4656,6 @@ 1727B39924C1368D00A4DBDC /* LayoutPreferencesView.swift in Sources */, 51A8FFEE24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */, 51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */, - 51E4993724A8680E00B667CB /* Reachability.swift in Sources */, 51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */, 51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */, 51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */, @@ -4833,7 +4816,6 @@ 65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */, 65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */, 65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */, - 65ED3FC7235DEF6C0081F399 /* Reachability.swift in Sources */, 65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */, 65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */, 515A5149243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */, @@ -5156,7 +5138,6 @@ 515A517C243E90260089E588 /* ExtensionPointManager.swift in Sources */, 51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */, 51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */, - 513228FC233037630033D4ED /* Reachability.swift in Sources */, 51C45259226508D300C03939 /* AppDefaults.swift in Sources */, 51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */, 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */, @@ -5190,7 +5171,6 @@ 8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */, 849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */, 84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */, - 513228FB233037630033D4ED /* Reachability.swift in Sources */, 845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */, 510C418224E5D1AE008226FD /* ExtensionFeedAddRequest.swift in Sources */, 516AE9DF2372269A007DEEAA /* IconImage.swift in Sources */, diff --git a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 77e685970..d58fa47fb 100644 --- a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/tid-kijyun/Kanna.git", "state": { "branch": null, - "revision": "609367a2cd84827a33383cf7923cb4fe8f69ee0a", - "version": "5.2.2" + "revision": "4a80ebe93b6966d5083394fcaaaff57a2fcec935", + "version": "5.2.3" } }, { @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSParser.git", "state": { "branch": null, - "revision": "d7b10caba6396b049a74b4bd11d649ba870331b4", - "version": "2.0.0-beta1" + "revision": "21d57ffb7ae744cf70bf6ddfb7ad8b7c102e05cf", + "version": "2.0.0-beta2" } }, { @@ -87,8 +87,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSWeb.git", "state": { "branch": null, - "revision": "d40861de4048610f81125484130c187edd1f30f3", - "version": "1.0.0-beta4" + "revision": "dd9e2a24bfc7f2fa5f59a443f543fc95191c9788", + "version": "1.0.0-beta7" } }, { @@ -96,8 +96,8 @@ "repositoryURL": "https://github.com/httpswift/swifter.git", "state": { "branch": null, - "revision": "8b5afb48ae64d4f729f0489ddcfe09c62b9c3687", - "version": "1.4.7" + "revision": "9483a5d459b45c3ffd059f7b55f9638e268632fd", + "version": "1.5.0" } } ] diff --git a/Shared/Extensions/CacheCleaner.swift b/Shared/Extensions/CacheCleaner.swift index 0bb4ce191..fa00603e7 100644 --- a/Shared/Extensions/CacheCleaner.swift +++ b/Shared/Extensions/CacheCleaner.swift @@ -8,6 +8,7 @@ import Foundation import os.log +import RSWeb struct CacheCleaner { diff --git a/Shared/Network/Reachability.swift b/Shared/Network/Reachability.swift deleted file mode 100644 index 6c8ffa2e4..000000000 --- a/Shared/Network/Reachability.swift +++ /dev/null @@ -1,406 +0,0 @@ -/* -Copyright (c) 2014, Ashley Mills -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -*/ - -import SystemConfiguration -import Foundation - -public enum ReachabilityError: Error { - case failedToCreateWithAddress(sockaddr, Int32) - case failedToCreateWithHostname(String, Int32) - case unableToSetCallback(Int32) - case unableToSetDispatchQueue(Int32) - case unableToGetFlags(Int32) -} - -@available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") -public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") - -public extension Notification.Name { - static let reachabilityChanged = Notification.Name("reachabilityChanged") -} - -public class Reachability { - - public typealias NetworkReachable = (Reachability) -> () - public typealias NetworkUnreachable = (Reachability) -> () - - @available(*, unavailable, renamed: "Connection") - public enum NetworkStatus: CustomStringConvertible { - case notReachable, reachableViaWiFi, reachableViaWWAN - public var description: String { - switch self { - case .reachableViaWWAN: return "Cellular" - case .reachableViaWiFi: return "WiFi" - case .notReachable: return "No Connection" - } - } - } - - public enum Connection: CustomStringConvertible { - @available(*, deprecated, renamed: "unavailable") - case none - case unavailable, wifi, cellular - public var description: String { - switch self { - case .cellular: return "Cellular" - case .wifi: return "WiFi" - case .unavailable: return "No Connection" - case .none: return "unavailable" - } - } - } - - public var whenReachable: NetworkReachable? - public var whenUnreachable: NetworkUnreachable? - - @available(*, deprecated, renamed: "allowsCellularConnection") - public let reachableOnWWAN: Bool = true - - /// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`) - public var allowsCellularConnection: Bool - - // The notification center on which "reachability changed" events are being posted - public var notificationCenter: NotificationCenter = NotificationCenter.default - - @available(*, deprecated, renamed: "connection.description") - public var currentReachabilityString: String { - return "\(connection)" - } - - @available(*, unavailable, renamed: "connection") - public var currentReachabilityStatus: Connection { - return connection - } - - public var connection: Connection { - if flags == nil { - try? setReachabilityFlags() - } - - switch flags?.connection { - case .unavailable?, nil: return .unavailable - case .none?: return .unavailable - case .cellular?: return allowsCellularConnection ? .cellular : .unavailable - case .wifi?: return .wifi - } - } - - fileprivate var isRunningOnDevice: Bool = { - #if targetEnvironment(simulator) - return false - #else - return true - #endif - }() - - fileprivate(set) var notifierRunning = false - fileprivate let reachabilityRef: SCNetworkReachability - fileprivate let reachabilitySerialQueue: DispatchQueue - fileprivate let notificationQueue: DispatchQueue? - fileprivate(set) var flags: SCNetworkReachabilityFlags? { - didSet { - guard flags != oldValue else { return } - notifyReachabilityChanged() - } - } - - required public init(reachabilityRef: SCNetworkReachability, - queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) { - self.allowsCellularConnection = true - self.reachabilityRef = reachabilityRef - self.reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue) - self.notificationQueue = notificationQueue - } - - public convenience init(hostname: String, - queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) throws { - guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { - throw ReachabilityError.failedToCreateWithHostname(hostname, SCError()) - } - self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) - } - - public convenience init(queueQoS: DispatchQoS = .default, - targetQueue: DispatchQueue? = nil, - notificationQueue: DispatchQueue? = .main) throws { - var zeroAddress = sockaddr() - zeroAddress.sa_len = UInt8(MemoryLayout.size) - zeroAddress.sa_family = sa_family_t(AF_INET) - - guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { - throw ReachabilityError.failedToCreateWithAddress(zeroAddress, SCError()) - } - - self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) - } - - deinit { - stopNotifier() - } -} - -public extension Reachability { - - // MARK: - *** Notifier methods *** - func startNotifier() throws { - guard !notifierRunning else { return } - - let callback: SCNetworkReachabilityCallBack = { (reachability, flags, info) in - guard let info = info else { return } - - // `weakifiedReachability` is guaranteed to exist by virtue of our - // retain/release callbacks which we provided to the `SCNetworkReachabilityContext`. - let weakifiedReachability = Unmanaged.fromOpaque(info).takeUnretainedValue() - - // The weak `reachability` _may_ no longer exist if the `Reachability` - // object has since been deallocated but a callback was already in flight. - weakifiedReachability.reachability?.flags = flags - } - - let weakifiedReachability = ReachabilityWeakifier(reachability: self) - let opaqueWeakifiedReachability = Unmanaged.passUnretained(weakifiedReachability).toOpaque() - - var context = SCNetworkReachabilityContext( - version: 0, - info: UnsafeMutableRawPointer(opaqueWeakifiedReachability), - retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - _ = unmanagedWeakifiedReachability.retain() - return UnsafeRawPointer(unmanagedWeakifiedReachability.toOpaque()) - }, - release: { (info: UnsafeRawPointer) -> Void in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - unmanagedWeakifiedReachability.release() - }, - copyDescription: { (info: UnsafeRawPointer) -> Unmanaged in - let unmanagedWeakifiedReachability = Unmanaged.fromOpaque(info) - let weakifiedReachability = unmanagedWeakifiedReachability.takeUnretainedValue() - let description = weakifiedReachability.reachability?.description ?? "nil" - return Unmanaged.passRetained(description as CFString) - } - ) - - if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { - stopNotifier() - throw ReachabilityError.unableToSetCallback(SCError()) - } - - if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { - stopNotifier() - throw ReachabilityError.unableToSetDispatchQueue(SCError()) - } - - // Perform an initial check - try setReachabilityFlags() - - notifierRunning = true - } - - func stopNotifier() { - defer { notifierRunning = false } - - SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) - SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) - } - - // MARK: - *** Connection test methods *** - @available(*, deprecated, message: "Please use `connection != .none`") - var isReachable: Bool { - return connection != .unavailable - } - - @available(*, deprecated, message: "Please use `connection == .cellular`") - var isReachableViaWWAN: Bool { - // Check we're not on the simulator, we're REACHABLE and check we're on WWAN - return connection == .cellular - } - - @available(*, deprecated, message: "Please use `connection == .wifi`") - var isReachableViaWiFi: Bool { - return connection == .wifi - } - - var description: String { - return flags?.description ?? "unavailable flags" - } -} - -fileprivate extension Reachability { - - func setReachabilityFlags() throws { - try reachabilitySerialQueue.sync { [unowned self] in - var flags = SCNetworkReachabilityFlags() - if !SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags) { - self.stopNotifier() - throw ReachabilityError.unableToGetFlags(SCError()) - } - - self.flags = flags - } - } - - - func notifyReachabilityChanged() { - let notify = { [weak self] in - guard let self = self else { return } - self.connection != .unavailable ? self.whenReachable?(self) : self.whenUnreachable?(self) - self.notificationCenter.post(name: .reachabilityChanged, object: self) - } - - // notify on the configured `notificationQueue`, or the caller's (i.e. `reachabilitySerialQueue`) - notificationQueue?.async(execute: notify) ?? notify() - } -} - -extension SCNetworkReachabilityFlags { - - typealias Connection = Reachability.Connection - - var connection: Connection { - guard isReachableFlagSet else { return .unavailable } - - // If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi - #if targetEnvironment(simulator) - return .wifi - #else - var connection = Connection.unavailable - - if !isConnectionRequiredFlagSet { - connection = .wifi - } - - if isConnectionOnTrafficOrDemandFlagSet { - if !isInterventionRequiredFlagSet { - connection = .wifi - } - } - - if isOnWWANFlagSet { - connection = .cellular - } - - return connection - #endif - } - - var isOnWWANFlagSet: Bool { - #if os(iOS) - return contains(.isWWAN) - #else - return false - #endif - } - var isReachableFlagSet: Bool { - return contains(.reachable) - } - var isConnectionRequiredFlagSet: Bool { - return contains(.connectionRequired) - } - var isInterventionRequiredFlagSet: Bool { - return contains(.interventionRequired) - } - var isConnectionOnTrafficFlagSet: Bool { - return contains(.connectionOnTraffic) - } - var isConnectionOnDemandFlagSet: Bool { - return contains(.connectionOnDemand) - } - var isConnectionOnTrafficOrDemandFlagSet: Bool { - return !intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty - } - var isTransientConnectionFlagSet: Bool { - return contains(.transientConnection) - } - var isLocalAddressFlagSet: Bool { - return contains(.isLocalAddress) - } - var isDirectFlagSet: Bool { - return contains(.isDirect) - } - var isConnectionRequiredAndTransientFlagSet: Bool { - return intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] - } - - var description: String { - let W = isOnWWANFlagSet ? "W" : "-" - let R = isReachableFlagSet ? "R" : "-" - let c = isConnectionRequiredFlagSet ? "c" : "-" - let t = isTransientConnectionFlagSet ? "t" : "-" - let i = isInterventionRequiredFlagSet ? "i" : "-" - let C = isConnectionOnTrafficFlagSet ? "C" : "-" - let D = isConnectionOnDemandFlagSet ? "D" : "-" - let l = isLocalAddressFlagSet ? "l" : "-" - let d = isDirectFlagSet ? "d" : "-" - - return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" - } -} - -/** - `ReachabilityWeakifier` weakly wraps the `Reachability` class - in order to break retain cycles when interacting with CoreFoundation. - - CoreFoundation callbacks expect a pair of retain/release whenever an - opaque `info` parameter is provided. These callbacks exist to guard - against memory management race conditions when invoking the callbacks. - - #### Race Condition - - If we passed `SCNetworkReachabilitySetCallback` a direct reference to our - `Reachability` class without also providing corresponding retain/release - callbacks, then a race condition can lead to crashes when: - - `Reachability` is deallocated on thread X - - A `SCNetworkReachability` callback(s) is already in flight on thread Y - - #### Retain Cycle - - If we pass `Reachability` to CoreFoundtion while also providing retain/ - release callbacks, we would create a retain cycle once CoreFoundation - retains our `Reachability` class. This fixes the crashes and his how - CoreFoundation expects the API to be used, but doesn't play nicely with - Swift/ARC. This cycle would only be broken after manually calling - `stopNotifier()` — `deinit` would never be called. - - #### ReachabilityWeakifier - - By providing both retain/release callbacks and wrapping `Reachability` in - a weak wrapper, we: - - interact correctly with CoreFoundation, thereby avoiding a crash. - See "Memory Management Programming Guide for Core Foundation". - - don't alter the public API of `Reachability.swift` in any way - - still allow for automatic stopping of the notifier on `deinit`. - */ -private class ReachabilityWeakifier { - weak var reachability: Reachability? - init(reachability: Reachability) { - self.reachability = reachability - } -} diff --git a/iOS/MasterFeed/RefreshProgressView.swift b/iOS/MasterFeed/RefreshProgressView.swift index add535b76..7886745f2 100644 --- a/iOS/MasterFeed/RefreshProgressView.swift +++ b/iOS/MasterFeed/RefreshProgressView.swift @@ -19,6 +19,9 @@ class RefreshProgressView: UIView { NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil) update() scheduleUpdateRefreshLabel() + + isAccessibilityElement = true + accessibilityTraits = [.updatesFrequently, .notEnabled] } func update() { @@ -109,6 +112,7 @@ private extension RefreshProgressView { label.text = "" } + accessibilityLabel = label.text } func scheduleUpdateRefreshLabel() { diff --git a/iOS/MasterFeed/RefreshProgressView.xib b/iOS/MasterFeed/RefreshProgressView.xib index 9c72af6c1..bab0971af 100644 --- a/iOS/MasterFeed/RefreshProgressView.xib +++ b/iOS/MasterFeed/RefreshProgressView.xib @@ -1,52 +1,61 @@ - + - + + + - - + + - - + + - - - - - - - - + + + + + + + + + - - - + + - + + + + + + diff --git a/iOS/ShareExtension/ShareViewController.swift b/iOS/ShareExtension/ShareViewController.swift index b1701c396..9da5495db 100644 --- a/iOS/ShareExtension/ShareViewController.swift +++ b/iOS/ShareExtension/ShareViewController.swift @@ -88,9 +88,12 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont return } self?.url = url + return }) } + // Reddit in particular doesn't pass the URL correctly and instead puts it in the contentText + url = URL(string: contentText) } override func isContentValid() -> Bool { @@ -103,7 +106,11 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont return } - let name = contentText.isEmpty ? nil : contentText + var name: String? = nil + if !contentText.mayBeURL { + name = contentText.isEmpty ? nil : contentText + } + let request = ExtensionFeedAddRequest(name: name, feedURL: url, destinationContainerID: containerID) ExtensionFeedAddRequestFile.save(request)