Merge branch 'main' of https://github.com/Ranchero-Software/NetNewsWire into main
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 674 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 1003 B |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 564 KiB |
@ -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"
|
||||||
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 674 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 1003 B |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 564 KiB |
@ -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 */,
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
|
import RSWeb
|
||||||
|
|
||||||
struct CacheCleaner {
|
struct CacheCleaner {
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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() {
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|