This commit is contained in:
Brent Simmons 2020-10-17 18:54:45 -07:00
commit c70231508b
51 changed files with 119 additions and 507 deletions

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import RSCore import RSCore
import RSWeb
import Articles import Articles
import ArticlesDatabase import ArticlesDatabase
@ -228,6 +229,8 @@ public final class AccountManager: UnreadCountProvider {
} }
public func refreshAll(errorHandler: @escaping (Error) -> Void, completion: (() -> Void)? = nil) { 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() let group = DispatchGroup()
activeAccounts.forEach { account in activeAccounts.forEach { account in
@ -249,6 +252,8 @@ public final class AccountManager: UnreadCountProvider {
} }
public func refreshAll(completion: (() -> Void)? = nil) { public func refreshAll(completion: (() -> Void)? = nil) {
guard let reachability = try? Reachability(hostname: "apple.com"), reachability.connection != .unavailable else { return }
var syncErrors = [AccountSyncError]() var syncErrors = [AccountSyncError]()
let group = DispatchGroup() let group = DispatchGroup()

View File

@ -33,6 +33,7 @@ typealias CloudKitRecordKey = (recordType: CKRecord.RecordType, recordID: CKReco
protocol CloudKitZone: class { protocol CloudKitZone: class {
static var zoneID: CKRecordZone.ID { get } static var zoneID: CKRecordZone.ID { get }
static var qualityOfService: QualityOfService { get }
var log: OSLog { get } var log: OSLog { get }
@ -56,6 +57,17 @@ protocol CloudKitZone: class {
extension CloudKitZone { 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 /// Reset the change token used to determine what point in time we are doing changes fetches
func resetChangeToken() { func resetChangeToken() {
changeToken = nil changeToken = nil
@ -239,7 +251,7 @@ extension CloudKitZone {
let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: [CKRecord.ID]()) let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: [CKRecord.ID]())
op.savePolicy = .ifServerRecordUnchanged op.savePolicy = .ifServerRecordUnchanged
op.isAtomic = false op.isAtomic = false
op.qualityOfService = .default op.qualityOfService = Self.qualityOfService
op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in
@ -352,7 +364,7 @@ extension CloudKitZone {
var records = [CKRecord]() var records = [CKRecord]()
let op = CKQueryOperation(query: ckQuery) let op = CKQueryOperation(query: ckQuery)
op.qualityOfService = .default op.qualityOfService = Self.qualityOfService
op.recordFetchedBlock = { record in op.recordFetchedBlock = { record in
records.append(record) records.append(record)
} }
@ -389,7 +401,7 @@ extension CloudKitZone {
var records = [CKRecord]() var records = [CKRecord]()
let op = CKQueryOperation(cursor: cursor) let op = CKQueryOperation(cursor: cursor)
op.qualityOfService = .default op.qualityOfService = Self.qualityOfService
op.recordFetchedBlock = { record in op.recordFetchedBlock = { record in
records.append(record) records.append(record)
} }
@ -471,7 +483,7 @@ extension CloudKitZone {
let op = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: recordIDsToDelete) let op = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: recordIDsToDelete)
op.savePolicy = .changedKeys op.savePolicy = .changedKeys
op.isAtomic = true op.isAtomic = true
op.qualityOfService = .default op.qualityOfService = Self.qualityOfService
op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in op.modifyRecordsCompletionBlock = { [weak self] (_, _, error) in
@ -568,7 +580,7 @@ extension CloudKitZone {
zoneConfig.previousServerChangeToken = changeToken zoneConfig.previousServerChangeToken = changeToken
let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [Self.zoneID], configurationsByRecordZoneID: [Self.zoneID: zoneConfig]) let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [Self.zoneID], configurationsByRecordZoneID: [Self.zoneID: zoneConfig])
op.fetchAllChanges = true op.fetchAllChanges = true
op.qualityOfService = .default op.qualityOfService = Self.qualityOfService
op.recordZoneChangeTokensUpdatedBlock = { zoneID, token, _ in op.recordZoneChangeTokensUpdatedBlock = { zoneID, token, _ in
savedChangeToken = token savedChangeToken = token

View File

@ -1,68 +1,68 @@
{ {
"images" : [ "images" : [
{ {
"size" : "16x16", "filename" : "Icon-MacOS-16x16@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_16x16.png", "scale" : "1x",
"scale" : "1x" "size" : "16x16"
}, },
{ {
"size" : "16x16", "filename" : "Icon-MacOS-16x16@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_16x16@2x.png", "scale" : "2x",
"scale" : "2x" "size" : "16x16"
}, },
{ {
"size" : "32x32", "filename" : "Icon-MacOS-32x32@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_32x32.png", "scale" : "1x",
"scale" : "1x" "size" : "32x32"
}, },
{ {
"size" : "32x32", "filename" : "Icon-MacOS-32x32@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_32x32@2x.png", "scale" : "2x",
"scale" : "2x" "size" : "32x32"
}, },
{ {
"size" : "128x128", "filename" : "Icon-MacOS-128x128@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_128x128.png", "scale" : "1x",
"scale" : "1x" "size" : "128x128"
}, },
{ {
"size" : "128x128", "filename" : "Icon-MacOS-128x128@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_128x128@2x.png", "scale" : "2x",
"scale" : "2x" "size" : "128x128"
}, },
{ {
"size" : "256x256", "filename" : "Icon-MacOS-256x256@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_256x256.png", "scale" : "1x",
"scale" : "1x" "size" : "256x256"
}, },
{ {
"size" : "256x256", "filename" : "Icon-MacOS-256x256@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_256x256@2x.png", "scale" : "2x",
"scale" : "2x" "size" : "256x256"
}, },
{ {
"size" : "512x512", "filename" : "Icon-MacOS-512x512@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_512x512.png", "scale" : "1x",
"scale" : "1x" "size" : "512x512"
}, },
{ {
"size" : "512x512", "filename" : "Icon-MacOS-512x512@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"filename" : "icon_512x512@2x.png", "scale" : "2x",
"scale" : "2x" "size" : "512x512"
} }
], ],
"info" : { "info" : {
"version" : 1, "author" : "xcode",
"author" : "xcode" "version" : 1
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

View File

@ -109,61 +109,61 @@
"size" : "1024x1024" "size" : "1024x1024"
}, },
{ {
"filename" : "icon_16x16.png", "filename" : "Icon-MacOS-16x16@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "icon_32x32.png", "filename" : "Icon-MacOS-16x16@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "16x16" "size" : "16x16"
}, },
{ {
"filename" : "icon_32x32-1.png", "filename" : "Icon-MacOS-32x32@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "icon_32x32@2x.png", "filename" : "Icon-MacOS-32x32@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "32x32" "size" : "32x32"
}, },
{ {
"filename" : "icon_128x128.png", "filename" : "Icon-MacOS-128x128@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "128x128" "size" : "128x128"
}, },
{ {
"filename" : "icon_128x128@2x.png", "filename" : "Icon-MacOS-128x128@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "128x128" "size" : "128x128"
}, },
{ {
"filename" : "icon_256x256.png", "filename" : "Icon-MacOS-256x256@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "256x256" "size" : "256x256"
}, },
{ {
"filename" : "icon_256x256@2x-1.png", "filename" : "Icon-MacOS-256x256@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "256x256" "size" : "256x256"
}, },
{ {
"filename" : "icon_512x512.png", "filename" : "Icon-MacOS-512x512@1x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "1x", "scale" : "1x",
"size" : "512x512" "size" : "512x512"
}, },
{ {
"filename" : "icon_512x512@2x.png", "filename" : "Icon-MacOS-512x512@2x.png.png",
"idiom" : "mac", "idiom" : "mac",
"scale" : "2x", "scale" : "2x",
"size" : "512x512" "size" : "512x512"

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

View File

@ -155,8 +155,6 @@
513146B3235A81A400387FDC /* AddWebFeedIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513146B1235A81A400387FDC /* AddWebFeedIntentHandler.swift */; }; 513146B3235A81A400387FDC /* AddWebFeedIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513146B1235A81A400387FDC /* AddWebFeedIntentHandler.swift */; };
51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; }; 51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; };
51314705235C41FC00387FDC /* 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 */; }; 51333D1624685D2E00EB5C91 /* AddRedditFeedWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */; };
51333D1724685D2E00EB5C91 /* 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 */; }; 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 */; }; 51E4993424A867E700B667CB /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; };
51E4993524A867E800B667CB /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */; }; 51E4993524A867E800B667CB /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */; };
51E4993624A867E800B667CB /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.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 */; }; 51E4993A24A8708800B667CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4993924A8708800B667CB /* AppDelegate.swift */; };
51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4993B24A8709900B667CB /* AppDelegate.swift */; }; 51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4993B24A8709900B667CB /* AppDelegate.swift */; };
51E4993D24A870F800B667CB /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.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 */; }; 65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; };
65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A975D1ED9EB72007D329B /* MainWindowController.swift */; }; 65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A975D1ED9EB72007D329B /* MainWindowController.swift */; };
65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.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 */; }; 65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */; };
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; }; 65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.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 = "<group>"; }; 513146B1235A81A400387FDC /* AddWebFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedIntentHandler.swift; sourceTree = "<group>"; };
51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; }; 51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; }; 51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; };
513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRedditFeedWindowController.swift; sourceTree = "<group>"; }; 51333D1524685D2E00EB5C91 /* AddRedditFeedWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRedditFeedWindowController.swift; sourceTree = "<group>"; };
51333D3A2468615D00EB5C91 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/AddRedditFeedSheet.xib; sourceTree = SOURCE_ROOT; }; 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 = "<group>"; }; 51392D1A24AC19A000BE0D35 /* SidebarExpandedContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarExpandedContainers.swift; sourceTree = "<group>"; };
@ -2387,14 +2381,6 @@
path = IntentsExtension; path = IntentsExtension;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
513228F1233037620033D4ED /* Network */ = {
isa = PBXGroup;
children = (
513228F2233037620033D4ED /* Reachability.swift */,
);
path = Network;
sourceTree = "<group>";
};
513C5CE7232571C2003D4054 /* ShareExtension */ = { 513C5CE7232571C2003D4054 /* ShareExtension */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -3309,7 +3295,6 @@
512E08DD22687FA000BDCFDD /* Tree */, 512E08DD22687FA000BDCFDD /* Tree */,
849A97561ED9EB0D007D329B /* Extensions */, 849A97561ED9EB0D007D329B /* Extensions */,
510C43F5243D0325009F70C3 /* ExtensionPoints */, 510C43F5243D0325009F70C3 /* ExtensionPoints */,
513228F1233037620033D4ED /* Network */,
511D43CE231FA51100FB1562 /* Resources */, 511D43CE231FA51100FB1562 /* Resources */,
); );
path = Shared; path = Shared;
@ -4554,7 +4539,6 @@
5177476724B3BE3400EB0F74 /* SettingsAboutModel.swift in Sources */, 5177476724B3BE3400EB0F74 /* SettingsAboutModel.swift in Sources */,
65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */, 65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */,
51E4990A24A808C500B667CB /* FeaturedImageDownloader.swift in Sources */, 51E4990A24A808C500B667CB /* FeaturedImageDownloader.swift in Sources */,
51E4993824A8680E00B667CB /* Reachability.swift in Sources */,
51E4993224A8676400B667CB /* FetchRequestQueue.swift in Sources */, 51E4993224A8676400B667CB /* FetchRequestQueue.swift in Sources */,
51E4991724A8090400B667CB /* ArticleUtilities.swift in Sources */, 51E4991724A8090400B667CB /* ArticleUtilities.swift in Sources */,
51E4991B24A8091000B667CB /* IconImage.swift in Sources */, 51E4991B24A8091000B667CB /* IconImage.swift in Sources */,
@ -4672,7 +4656,6 @@
1727B39924C1368D00A4DBDC /* LayoutPreferencesView.swift in Sources */, 1727B39924C1368D00A4DBDC /* LayoutPreferencesView.swift in Sources */,
51A8FFEE24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */, 51A8FFEE24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */,
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */, 51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */, 51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */,
51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */, 51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */,
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */, 51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
@ -4833,7 +4816,6 @@
65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */, 65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */,
65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */, 65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */,
65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */, 65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */,
65ED3FC7235DEF6C0081F399 /* Reachability.swift in Sources */,
65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */, 65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */,
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */, 65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */,
515A5149243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */, 515A5149243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */,
@ -5156,7 +5138,6 @@
515A517C243E90260089E588 /* ExtensionPointManager.swift in Sources */, 515A517C243E90260089E588 /* ExtensionPointManager.swift in Sources */,
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */, 51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */,
51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */, 51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */,
513228FC233037630033D4ED /* Reachability.swift in Sources */,
51C45259226508D300C03939 /* AppDefaults.swift in Sources */, 51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */, 51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */,
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */, 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
@ -5190,7 +5171,6 @@
8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */, 8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */,
849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */, 849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */,
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */, 84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */,
513228FB233037630033D4ED /* Reachability.swift in Sources */,
845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */, 845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */,
510C418224E5D1AE008226FD /* ExtensionFeedAddRequest.swift in Sources */, 510C418224E5D1AE008226FD /* ExtensionFeedAddRequest.swift in Sources */,
516AE9DF2372269A007DEEAA /* IconImage.swift in Sources */, 516AE9DF2372269A007DEEAA /* IconImage.swift in Sources */,

View File

@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/tid-kijyun/Kanna.git", "repositoryURL": "https://github.com/tid-kijyun/Kanna.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "609367a2cd84827a33383cf7923cb4fe8f69ee0a", "revision": "4a80ebe93b6966d5083394fcaaaff57a2fcec935",
"version": "5.2.2" "version": "5.2.3"
} }
}, },
{ {
@ -69,8 +69,8 @@
"repositoryURL": "https://github.com/Ranchero-Software/RSParser.git", "repositoryURL": "https://github.com/Ranchero-Software/RSParser.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "d7b10caba6396b049a74b4bd11d649ba870331b4", "revision": "21d57ffb7ae744cf70bf6ddfb7ad8b7c102e05cf",
"version": "2.0.0-beta1" "version": "2.0.0-beta2"
} }
}, },
{ {
@ -87,8 +87,8 @@
"repositoryURL": "https://github.com/Ranchero-Software/RSWeb.git", "repositoryURL": "https://github.com/Ranchero-Software/RSWeb.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "d40861de4048610f81125484130c187edd1f30f3", "revision": "dd9e2a24bfc7f2fa5f59a443f543fc95191c9788",
"version": "1.0.0-beta4" "version": "1.0.0-beta7"
} }
}, },
{ {
@ -96,8 +96,8 @@
"repositoryURL": "https://github.com/httpswift/swifter.git", "repositoryURL": "https://github.com/httpswift/swifter.git",
"state": { "state": {
"branch": null, "branch": null,
"revision": "8b5afb48ae64d4f729f0489ddcfe09c62b9c3687", "revision": "9483a5d459b45c3ffd059f7b55f9638e268632fd",
"version": "1.4.7" "version": "1.5.0"
} }
} }
] ]

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import os.log import os.log
import RSWeb
struct CacheCleaner { struct CacheCleaner {

View File

@ -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<sockaddr>.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<ReachabilityWeakifier>.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<ReachabilityWeakifier>.passUnretained(weakifiedReachability).toOpaque()
var context = SCNetworkReachabilityContext(
version: 0,
info: UnsafeMutableRawPointer(opaqueWeakifiedReachability),
retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info)
_ = unmanagedWeakifiedReachability.retain()
return UnsafeRawPointer(unmanagedWeakifiedReachability.toOpaque())
},
release: { (info: UnsafeRawPointer) -> Void in
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info)
unmanagedWeakifiedReachability.release()
},
copyDescription: { (info: UnsafeRawPointer) -> Unmanaged<CFString> in
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.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
}
}

View File

@ -19,6 +19,9 @@ class RefreshProgressView: UIView {
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
update() update()
scheduleUpdateRefreshLabel() scheduleUpdateRefreshLabel()
isAccessibilityElement = true
accessibilityTraits = [.updatesFrequently, .notEnabled]
} }
func update() { func update() {
@ -109,6 +112,7 @@ private extension RefreshProgressView {
label.text = "" label.text = ""
} }
accessibilityLabel = label.text
} }
func scheduleUpdateRefreshLabel() { func scheduleUpdateRefreshLabel() {

View File

@ -1,52 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="RefreshProgressView" customModule="NetNewsWire" customModuleProvider="target"> <view contentMode="scaleToFill" id="ejl-zC-eNy" customClass="RefreshProgressView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/> <rect key="frame" x="0.0" y="0.0" width="461" height="90"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="gKH-fc-zh7" customClass="RoundedProgressView" customModule="NetNewsWire" customModuleProvider="target"> <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="Ds3-59-ooT" customClass="RoundedProgressView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="137.5" y="27.5" width="100" height="5"/> <rect key="frame" x="180.5" y="42.5" width="100" height="5"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="5" id="OCl-qi-owb"/> <constraint firstAttribute="width" constant="100" id="ReS-sT-7EN"/>
<constraint firstAttribute="width" constant="100" id="v3Q-GE-krS"/> <constraint firstAttribute="height" constant="5" id="oDX-bb-24H"/>
</constraints> </constraints>
</progressView> </progressView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="07B-Zy-FCt"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7mJ-VZ-zqU">
<rect key="frame" x="187.5" y="30" width="0.0" height="0.0"/> <rect key="frame" x="214" y="34" width="33" height="22"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/> <fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <viewLayoutGuide key="safeArea" id="sNo-8i-tO3"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstItem="07B-Zy-FCt" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="01K-1E-PEm"/> <constraint firstItem="Ds3-59-ooT" firstAttribute="centerX" secondItem="ejl-zC-eNy" secondAttribute="centerX" id="5Rv-6l-HSL"/>
<constraint firstItem="07B-Zy-FCt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="vUN-kp-3ea" secondAttribute="leading" id="6eL-GN-NIE"/> <constraint firstItem="Ds3-59-ooT" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="ejl-zC-eNy" secondAttribute="leading" id="Bck-uf-0G7"/>
<constraint firstItem="07B-Zy-FCt" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="QJ0-os-kZt"/> <constraint firstItem="7mJ-VZ-zqU" firstAttribute="bottom" secondItem="sNo-8i-tO3" secondAttribute="bottom" id="DVn-hI-PhH"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="gKH-fc-zh7" secondAttribute="trailing" id="SbS-0T-bdo"/> <constraint firstItem="7mJ-VZ-zqU" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="sNo-8i-tO3" secondAttribute="leading" id="Sbp-yf-ts9"/>
<constraint firstItem="gKH-fc-zh7" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="iN0-l3-epB" secondAttribute="leading" id="V0P-ix-fa3"/> <constraint firstItem="7mJ-VZ-zqU" firstAttribute="centerY" secondItem="ejl-zC-eNy" secondAttribute="centerY" id="Shb-X2-Fwc"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="07B-Zy-FCt" secondAttribute="trailing" id="Zor-53-U98"/> <constraint firstItem="7mJ-VZ-zqU" firstAttribute="centerX" secondItem="ejl-zC-eNy" secondAttribute="centerX" id="lFg-fm-YmV"/>
<constraint firstItem="gKH-fc-zh7" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="eX0-hg-5sb"/> <constraint firstItem="sNo-8i-tO3" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="7mJ-VZ-zqU" secondAttribute="trailing" id="mZ2-XG-Kvg"/>
<constraint firstItem="gKH-fc-zh7" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="mnf-7m-knt"/> <constraint firstItem="Ds3-59-ooT" firstAttribute="centerY" secondItem="ejl-zC-eNy" secondAttribute="centerY" id="tIh-lb-KbY"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Ds3-59-ooT" secondAttribute="trailing" id="vSU-N6-Sk5"/>
</constraints> </constraints>
<nil key="simulatedTopBarMetrics"/> <nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/> <nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<connections> <connections>
<outlet property="label" destination="07B-Zy-FCt" id="UEr-Dh-NBo"/> <outlet property="label" destination="7mJ-VZ-zqU" id="MHr-r4-qop"/>
<outlet property="progressView" destination="gKH-fc-zh7" id="Una-CD-Zi8"/> <outlet property="progressView" destination="Ds3-59-ooT" id="TjM-db-LxM"/>
</connections> </connections>
<point key="canvasLocation" x="-151" y="6"/> <point key="canvasLocation" x="-75" y="-117"/>
</view> </view>
</objects> </objects>
<resources>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document> </document>

View File

@ -88,9 +88,12 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont
return return
} }
self?.url = url 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 { override func isContentValid() -> Bool {
@ -103,7 +106,11 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont
return 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) let request = ExtensionFeedAddRequest(name: name, feedURL: url, destinationContainerID: containerID)
ExtensionFeedAddRequestFile.save(request) ExtensionFeedAddRequestFile.save(request)