Logging changes

- Adds `Logging` protocol
- Moves to Swift-style `OSLog` usage

os_log to Logger

os_log audit

Replacment of os.log with Logging
This commit is contained in:
Stuart Breckenridge 2022-08-03 15:37:12 +08:00
parent bff3c26490
commit 0dd4689bf0
No known key found for this signature in database
GPG Key ID: 64DCE361D27B805B
52 changed files with 270 additions and 271 deletions

View File

@ -26,7 +26,7 @@ dependencies.append(contentsOf: [
let package = Package(
name: "Account",
platforms: [.macOS(SupportedPlatform.MacOSVersion.v10_15), .iOS(SupportedPlatform.IOSVersion.v13)],
platforms: [.macOS(SupportedPlatform.MacOSVersion.v11), .iOS(SupportedPlatform.IOSVersion.v14)],
products: [
.library(
name: "Account",

View File

@ -17,7 +17,6 @@ import RSParser
import RSDatabase
import ArticlesDatabase
import RSWeb
import os.log
import Secrets
// Main thread only.
@ -91,8 +90,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return defaultName
}()
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "account")
public var isDeleted = false

View File

@ -7,12 +7,9 @@
//
import Foundation
import os.log
import RSCore
final class AccountMetadataFile {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "accountMetadataFile")
final class AccountMetadataFile: Logging {
private let fileURL: URL
private let account: Account
@ -50,8 +47,8 @@ final class AccountMetadataFile {
do {
let data = try encoder.encode(account.metadata)
try data.write(to: fileURL)
} catch let error as NSError {
os_log(.error, log: log, "Save to disk failed: %@.", error.localizedDescription)
} catch let error {
logger.error("Save to disk failed: \(error.localizedDescription)")
}
}

View File

@ -9,7 +9,6 @@
import Foundation
import CloudKit
import SystemConfiguration
import os.log
import SyncDatabase
import RSCore
import RSParser
@ -27,9 +26,7 @@ enum CloudKitAccountDelegateError: LocalizedError {
}
}
final class CloudKitAccountDelegate: AccountDelegate {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
final class CloudKitAccountDelegate: AccountDelegate, Logging {
private let database: SyncDatabase
@ -318,10 +315,10 @@ final class CloudKitAccountDelegate: AccountDelegate {
for webFeed in webFeeds {
group.enter()
self.removeWebFeedFromCloud(for: account, with: webFeed, from: folder) { result in
self.removeWebFeedFromCloud(for: account, with: webFeed, from: folder) { [weak self] result in
group.leave()
if case .failure(let error) = result {
os_log(.error, log: self.log, "Remove folder, remove webfeed error: %@.", error.localizedDescription)
self?.logger.error("Remove folder, remove webfeed error: \(error.localizedDescription, privacy: .public)")
errorOccurred = true
}
}
@ -380,14 +377,14 @@ final class CloudKitAccountDelegate: AccountDelegate {
folder.topLevelWebFeeds.remove(feed)
group.enter()
self.restoreWebFeed(for: account, feed: feed, container: folder) { result in
self.refreshProgress.completeTask()
self.restoreWebFeed(for: account, feed: feed, container: folder) { [weak self] result in
self?.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
break
case .failure(let error):
os_log(.error, log: self.log, "Restore folder feed error: %@.", error.localizedDescription)
self?.logger.error("Restore folder feed error: \(error.localizedDescription, privacy: .public)")
}
}
@ -437,13 +434,13 @@ final class CloudKitAccountDelegate: AccountDelegate {
// Check to see if this is a new account and initialize anything we need
if account.externalID == nil {
accountZone.findOrCreateAccount() { result in
accountZone.findOrCreateAccount() { [weak self] result in
switch result {
case .success(let externalID):
account.externalID = externalID
self.initialRefreshAll(for: account) { _ in }
self?.initialRefreshAll(for: account) { _ in }
case .failure(let error):
os_log(.error, log: self.log, "Error adding account container: %@", error.localizedDescription)
self?.logger.error("Error adding account container: \(error.localizedDescription, privacy: .public)")
}
}
accountZone.subscribeToZoneChanges()
@ -589,7 +586,7 @@ private extension CloudKitAccountDelegate {
group.leave()
}
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed refresh update error: %@.", error.localizedDescription)
self.logger.error("CloudKit Feed refresh update error: \(error.localizedDescription, privacy: .public)")
self.refreshProgress.completeTask()
group.leave()
}
@ -597,7 +594,7 @@ private extension CloudKitAccountDelegate {
}
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed refresh error: %@.", error.localizedDescription)
self.logger.error("CloudKit Feed refresh error: \(error.localizedDescription, privacy: .public)")
feedProviderError = error
self.refreshProgress.completeTask()
group.leave()
@ -804,12 +801,12 @@ private extension CloudKitAccountDelegate {
case .success:
self.articlesZone.fetchChangesInZone() { _ in }
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed send articles error: %@.", error.localizedDescription)
self.logger.error("CloudKit Feed send articles error: \(error.localizedDescription, privacy: .public)")
}
}
}
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed send articles error: %@.", error.localizedDescription)
self.logger.error("CloudKit Feed send articles error: \(error.localizedDescription, privacy: .public)")
}
}
}

View File

@ -7,7 +7,6 @@
//
import Foundation
import os.log
import RSCore
import RSParser
import RSWeb
@ -16,10 +15,8 @@ import SyncDatabase
import Articles
import ArticlesDatabase
class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate, Logging {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
weak var account: Account?
var database: SyncDatabase
weak var articlesZone: CloudKitArticlesZone?
@ -49,12 +46,12 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
}
case .failure(let error):
os_log(.error, log: self.log, "Error occurred getting pending starred records: %@", error.localizedDescription)
self.logger.error("Error occurred getting pending starred records: \(error.localizedDescription)")
completion(.failure(CloudKitZoneError.unknown))
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error occurred getting pending read status records: %@", error.localizedDescription)
self.logger.error("Error occurred getting pending read status records: \(error.localizedDescription)")
completion(.failure(CloudKitZoneError.unknown))
}
@ -102,7 +99,7 @@ private extension CloudKitArticlesZoneDelegate {
account?.markAsUnread(updateableUnreadArticleIDs) { result in
if case .failure(let databaseError) = result {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unread statuses: %@", databaseError.localizedDescription)
self.logger.error("Error occurred while storing unread statuses: \(databaseError.localizedDescription)")
}
group.leave()
}
@ -111,7 +108,7 @@ private extension CloudKitArticlesZoneDelegate {
account?.markAsRead(updateableReadArticleIDs) { result in
if case .failure(let databaseError) = result {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing read statuses: %@", databaseError.localizedDescription)
self.logger.error("Error occurred while storing read statuses: \(databaseError.localizedDescription)")
}
group.leave()
}
@ -120,7 +117,7 @@ private extension CloudKitArticlesZoneDelegate {
account?.markAsUnstarred(updateableUnstarredArticleIDs) { result in
if case .failure(let databaseError) = result {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unstarred statuses: %@", databaseError.localizedDescription)
self.logger.error("Error occurred while storing unstarred statuses: \(databaseError.localizedDescription)")
}
group.leave()
}
@ -129,7 +126,7 @@ private extension CloudKitArticlesZoneDelegate {
account?.markAsStarred(updateableStarredArticleIDs) { result in
if case .failure(let databaseError) = result {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing starred statuses: %@", databaseError.localizedDescription)
self.logger.error("Error occurred while stroing starred records: \(databaseError.localizedDescription)")
}
group.leave()
}
@ -155,7 +152,7 @@ private extension CloudKitArticlesZoneDelegate {
}
case .failure(let databaseError):
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing articles: %@", databaseError.localizedDescription)
self.logger.error("Error occurred while storing articles: \(databaseError.localizedDescription)")
group.leave()
}
}

View File

@ -7,13 +7,10 @@
//
import Foundation
import os.log
import RSCore
class CloudKitReceiveStatusOperation: MainThreadOperation {
class CloudKitReceiveStatusOperation: MainThreadOperation, Logging {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
// MainThreadOperation
public var isCanceled = false
public var id: Int?
@ -33,15 +30,15 @@ class CloudKitReceiveStatusOperation: MainThreadOperation {
return
}
os_log(.debug, log: log, "Refreshing article statuses...")
logger.debug("Refreshing article statuses...")
articlesZone.refreshArticles() { result in
os_log(.debug, log: self.log, "Done refreshing article statuses.")
self.logger.debug("Done refreshing article statuses.")
switch result {
case .success:
self.operationDelegate?.operationDidComplete(self)
case .failure(let error):
os_log(.error, log: self.log, "Receive status error: %@.", error.localizedDescription)
self.logger.debug("Receive status error: \(error.localizedDescription)")
self.operationDelegate?.cancelOperation(self)
}
}

View File

@ -7,14 +7,10 @@
//
import Foundation
import os.log
import RSCore
class CloudKitRemoteNotificationOperation: MainThreadOperation {
class CloudKitRemoteNotificationOperation: MainThreadOperation, Logging {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
// MainThreadOperation
public var isCanceled = false
public var id: Int?
@ -37,12 +33,12 @@ class CloudKitRemoteNotificationOperation: MainThreadOperation {
self.operationDelegate?.operationDidComplete(self)
return
}
os_log(.debug, log: log, "Processing remote notification...")
logger.debug("Processing remote notification...")
accountZone.receiveRemoteNotification(userInfo: userInfo) {
articlesZone.receiveRemoteNotification(userInfo: self.userInfo) {
os_log(.debug, log: self.log, "Done processing remote notification.")
self.logger.debug("Done processing remote notification.")
self.operationDelegate?.operationDidComplete(self)
}
}

View File

@ -8,14 +8,12 @@
import Foundation
import Articles
import os.log
import RSCore
import RSWeb
import SyncDatabase
class CloudKitSendStatusOperation: MainThreadOperation {
class CloudKitSendStatusOperation: MainThreadOperation, Logging {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
private let blockSize = 150
// MainThreadOperation
@ -40,7 +38,7 @@ class CloudKitSendStatusOperation: MainThreadOperation {
}
func run() {
os_log(.debug, log: log, "Sending article statuses...")
logger.debug("Sending article statuses...")
if showProgress {
@ -51,7 +49,7 @@ class CloudKitSendStatusOperation: MainThreadOperation {
self.refreshProgress?.addToNumberOfTasksAndRemaining(ticks)
self.selectForProcessing()
case .failure(let databaseError):
os_log(.error, log: self.log, "Send status count pending error: %@.", databaseError.localizedDescription)
self.logger.error("Send status count pending error: \(databaseError.localizedDescription)")
self.operationDelegate?.cancelOperation(self)
}
}
@ -77,7 +75,7 @@ private extension CloudKitSendStatusOperation {
if self.showProgress {
self.refreshProgress?.completeTask()
}
os_log(.debug, log: self.log, "Done sending article statuses.")
self.logger.debug("Done sending article statuses.")
self.operationDelegate?.operationDidComplete(self)
}
@ -95,7 +93,7 @@ private extension CloudKitSendStatusOperation {
}
case .failure(let databaseError):
os_log(.error, log: self.log, "Send status error: %@.", databaseError.localizedDescription)
self.logger.error("Send status error: \(databaseError.localizedDescription)")
self.operationDelegate?.cancelOperation(self)
}
}
@ -125,7 +123,7 @@ private extension CloudKitSendStatusOperation {
if self.showProgress && self.refreshProgress?.numberRemaining ?? 0 > 1 {
self.refreshProgress?.completeTask()
}
os_log(.debug, log: self.log, "Done sending article status block...")
self.logger.debug("Done sending article status block...")
completion(stop)
}
@ -147,7 +145,7 @@ private extension CloudKitSendStatusOperation {
case .failure(let error):
self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in
self.processAccountError(account, error)
os_log(.error, log: self.log, "Send article status modify articles error: %@.", error.localizedDescription)
self.logger.error("Send article status modify articles error: \(error.localizedDescription)")
completion(true)
}
}
@ -161,7 +159,7 @@ private extension CloudKitSendStatusOperation {
processWithArticles(articles)
case .failure(let databaseError):
self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID })) { _ in
os_log(.error, log: self.log, "Send article status fetch articles error: %@.", databaseError.localizedDescription)
self.logger.error("Send article status fetch articles error: \(databaseError.localizedDescription)")
completion(true)
}
}

View File

@ -7,7 +7,6 @@
//
import Foundation
import os.log
import OAuthSwift
import Secrets
import RSCore
@ -40,8 +39,6 @@ public enum RedditFeedType: Int {
public final class RedditFeedProvider: FeedProvider, RedditFeedProviderTokenRefreshOperationDelegate {
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "RedditFeedProvider")
private static let homeURL = "https://www.reddit.com"
private static let server = "www.reddit.com"
private static let apiBase = "https://oauth.reddit.com"

View File

@ -19,9 +19,7 @@ protocol RedditFeedProviderTokenRefreshOperationDelegate: AnyObject {
var oauthSwift: OAuth2Swift? { get }
}
class RedditFeedProviderTokenRefreshOperation: MainThreadOperation {
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "RedditFeedProvider")
class RedditFeedProviderTokenRefreshOperation: MainThreadOperation, Logging {
public var isCanceled = false
public var id: Int?
@ -49,7 +47,7 @@ class RedditFeedProviderTokenRefreshOperation: MainThreadOperation {
return
}
os_log(.debug, log: self.log, "Access token expired, attempting to renew...")
logger.debug("Access token expired, attempting to renew...")
delegate.oauthSwift?.renewAccessToken(withRefreshToken: delegate.oauthRefreshToken) { [weak self] result in
guard let self = self else { return }
@ -61,7 +59,7 @@ class RedditFeedProviderTokenRefreshOperation: MainThreadOperation {
do {
try RedditFeedProvider.storeCredentials(username: username, oauthToken: delegate.oauthToken, oauthRefreshToken: delegate.oauthRefreshToken)
delegate.oauthTokenLastRefresh = Date()
os_log(.debug, log: self.log, "Access token renewed.")
self.logger.debug("Access token renewed.")
} catch {
self.error = error
self.operationDelegate?.operationDidComplete(self)

View File

@ -12,7 +12,6 @@ import RSDatabase
import RSParser
import RSWeb
import SyncDatabase
import os.log
import Secrets
public enum FeedbinAccountDelegateError: String, Error {
@ -20,12 +19,11 @@ public enum FeedbinAccountDelegateError: String, Error {
case unknown = "An unknown error occurred."
}
final class FeedbinAccountDelegate: AccountDelegate {
final class FeedbinAccountDelegate: AccountDelegate, Logging {
private let database: SyncDatabase
private let caller: FeedbinAPICaller
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Feedbin")
let behaviors: AccountBehaviors = [.disallowFeedCopyInRootFolder]
let server: String? = "api.feedbin.com"
@ -132,7 +130,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Sending article statuses...")
logger.debug("Sending article statuses")
database.selectForProcessing { result in
@ -178,7 +176,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
self.logger.debug("Done sending article statuses.")
if errorOccurred {
completion(.failure(FeedbinAccountDelegateError.unknown))
} else {
@ -198,7 +196,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Refreshing article statuses...")
logger.debug("Refreshing article statuses...")
let group = DispatchGroup()
var errorOccurred = false
@ -212,7 +210,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
}
case .failure(let error):
errorOccurred = true
os_log(.info, log: self.log, "Retrieving unread entries failed: %@.", error.localizedDescription)
self.logger.error("Retrieving unread entries failed: \(error.localizedDescription, privacy: .public)")
group.leave()
}
@ -227,14 +225,14 @@ final class FeedbinAccountDelegate: AccountDelegate {
}
case .failure(let error):
errorOccurred = true
os_log(.info, log: self.log, "Retrieving starred entries failed: %@.", error.localizedDescription)
self.logger.error("Retrieving starred entries failed: \(error.localizedDescription, privacy: .public)")
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done refreshing article statuses.")
self.logger.debug("Done refreshing article statuses.")
if errorOccurred {
completion(.failure(FeedbinAccountDelegateError.unknown))
} else {
@ -260,7 +258,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
return
}
os_log(.debug, log: log, "Begin importing OPML...")
logger.debug("Begin importing OPML...")
isOPMLImportInProgress = true
refreshProgress.addToNumberOfTasksAndRemaining(1)
@ -268,7 +266,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
switch result {
case .success(let importResult):
if importResult.complete {
os_log(.debug, log: self.log, "Import OPML done.")
self.logger.debug("Import OPML done.")
self.refreshProgress.completeTask()
self.isOPMLImportInProgress = false
DispatchQueue.main.async {
@ -278,7 +276,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
self.checkImportResult(opmlImportResultID: importResult.importResultID, completion: completion)
}
case .failure(let error):
os_log(.debug, log: self.log, "Import OPML failed.")
self.logger.error("Import OPML failed: \(error.localizedDescription, privacy: .public)")
self.refreshProgress.completeTask()
self.isOPMLImportInProgress = false
DispatchQueue.main.async {
@ -352,7 +350,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
}
case .failure(let error):
os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
@ -371,7 +369,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
account.clearWebFeedMetadata(feed)
}
case .failure(let error):
os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
@ -541,7 +539,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
case .success:
break
case .failure(let error):
os_log(.error, log: self.log, "Restore folder feed error: %@.", error.localizedDescription)
self.logger.error("Restore folder feed error: \(error.localizedDescription, privacy: .public)")
}
}
@ -624,13 +622,13 @@ private extension FeedbinAccountDelegate {
Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { timer in
os_log(.debug, log: self.log, "Checking status of OPML import...")
self.logger.debug("Checking status of OPML import...")
self.caller.retrieveOPMLImportResult(importID: opmlImportResultID) { result in
switch result {
case .success(let importResult):
if let result = importResult, result.complete {
os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.")
self.logger.debug("Checking status of OPML import successfully completed.")
timer.invalidate()
self.refreshProgress.completeTask()
self.isOPMLImportInProgress = false
@ -639,7 +637,7 @@ private extension FeedbinAccountDelegate {
}
}
case .failure(let error):
os_log(.debug, log: self.log, "Import OPML check failed.")
self.logger.debug("Import OPML check failed.")
timer.invalidate()
self.refreshProgress.completeTask()
self.isOPMLImportInProgress = false
@ -772,7 +770,7 @@ private extension FeedbinAccountDelegate {
guard let tags = tags else { return }
assert(Thread.isMainThread)
os_log(.debug, log: log, "Syncing folders with %ld tags.", tags.count)
logger.debug("Syncing folders with \(tags.count, privacy: .public) tags.")
let tagNames = tags.map { $0.name }
@ -811,7 +809,7 @@ private extension FeedbinAccountDelegate {
guard let subscriptions = subscriptions else { return }
assert(Thread.isMainThread)
os_log(.debug, log: log, "Syncing feeds with %ld subscriptions.", subscriptions.count)
logger.debug("Syncing feeds with \(subscriptions.count, privacy: .public) subscriptions.")
let subFeedIds = subscriptions.map { String($0.feedID) }
@ -865,7 +863,7 @@ private extension FeedbinAccountDelegate {
guard let taggings = taggings else { return }
assert(Thread.isMainThread)
os_log(.debug, log: log, "Syncing taggings with %ld taggings.", taggings.count)
logger.debug("Syncing taggings with \(taggings.count) taggings.")
// Set up some structures to make syncing easier
let folderDict = nameToFolderDictionary(with: account.folders)
@ -961,7 +959,7 @@ private extension FeedbinAccountDelegate {
group.leave()
case .failure(let error):
errorOccurred = true
os_log(.error, log: self.log, "Article status sync call failed: %@.", error.localizedDescription)
self.logger.error("Article status sync call failed: \(error.localizedDescription, privacy: .public)")
self.database.resetSelectedForProcessing(articleIDGroup.map { String($0) } )
group.leave()
}
@ -1122,7 +1120,7 @@ private extension FeedbinAccountDelegate {
func refreshArticles(_ account: Account, completion: @escaping VoidResultCompletionBlock) {
os_log(.debug, log: log, "Refreshing articles...")
logger.debug("Refreshing articles...")
caller.retrieveEntries() { result in
@ -1143,7 +1141,7 @@ private extension FeedbinAccountDelegate {
}
self.refreshArticles(account, page: page, updateFetchDate: updateFetchDate) { result in
os_log(.debug, log: self.log, "Done refreshing articles.")
self.logger.debug("Done refreshing articles.")
switch result {
case .success:
completion(.success(()))
@ -1160,7 +1158,7 @@ private extension FeedbinAccountDelegate {
}
func refreshMissingArticles(_ account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Refreshing missing articles...")
logger.debug("Refreshing missing articles...")
account.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate { result in
@ -1187,7 +1185,7 @@ private extension FeedbinAccountDelegate {
case .failure(let error):
errorOccurred = true
os_log(.error, log: self.log, "Refresh missing articles failed: %@.", error.localizedDescription)
self.logger.error("Refreshing missing articles failed: \(error.localizedDescription, privacy: .public)")
group.leave()
}
}
@ -1195,7 +1193,7 @@ private extension FeedbinAccountDelegate {
group.notify(queue: DispatchQueue.main) {
self.refreshProgress.completeTask()
os_log(.debug, log: self.log, "Done refreshing missing articles.")
self.logger.debug("Done refreshing missing articles.")
if errorOccurred {
completion(.failure(FeedbinAccountDelegateError.unknown))
} else {
@ -1312,7 +1310,7 @@ private extension FeedbinAccountDelegate {
case .success(let pendingArticleIDs):
process(pendingArticleIDs)
case .failure(let error):
os_log(.error, log: self.log, "Sync Article Read Status failed: %@.", error.localizedDescription)
self.logger.error("Sync Articles Read Status failed: \(error.localizedDescription, privacy: .public)")
}
}
@ -1364,7 +1362,7 @@ private extension FeedbinAccountDelegate {
case .success(let pendingArticleIDs):
process(pendingArticleIDs)
case .failure(let error):
os_log(.error, log: self.log, "Sync Article Starred Status failed: %@.", error.localizedDescription)
self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -7,7 +7,6 @@
//
import Foundation
import os.log
import RSCore
import RSParser
import Articles
@ -19,9 +18,7 @@ public enum LocalAccountDelegateError: String, Error {
case invalidParameter = "An invalid parameter was used."
}
final class LocalAccountDelegate: AccountDelegate {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "LocalAccount")
final class LocalAccountDelegate: AccountDelegate, Logging {
weak var account: Account?
@ -68,7 +65,7 @@ final class LocalAccountDelegate: AccountDelegate {
group.leave()
}
case .failure(let error):
os_log(.error, log: self.log, "Feed Provider refresh error: %@.", error.localizedDescription)
self.logger.error("Feed Provided refresh error: \(error.localizedDescription)")
feedProviderError = error
self.refreshProgress.completeTask()
group.leave()

View File

@ -7,13 +7,10 @@
//
import Foundation
import os.log
import RSCore
import RSParser
final class OPMLFile {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "opmlFile")
final class OPMLFile: Logging {
private let fileURL: URL
private let account: Account
@ -50,8 +47,8 @@ final class OPMLFile {
do {
try opmlDocumentString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch let error as NSError {
os_log(.error, log: log, "OPML save to disk failed: %@.", error.localizedDescription)
} catch let error {
logger.error("OPML save to disk failed: \(error.localizedDescription)")
}
}
@ -76,7 +73,7 @@ private extension OPMLFile {
do {
fileData = try Data(contentsOf: fileURL)
} catch {
os_log(.error, log: log, "OPML read from disk failed: %@.", error.localizedDescription)
logger.error("OPML read from disk failed: \(error.localizedDescription)")
}
return fileData
@ -89,7 +86,7 @@ private extension OPMLFile {
do {
opmlDocument = try RSOPMLParser.parseOPML(with: parserData)
} catch {
os_log(.error, log: log, "OPML Import failed: %@.", error.localizedDescription)
logger.error("OPML Import failed: \(error.localizedDescription)")
return nil
}

View File

@ -11,7 +11,6 @@ import RSCore
import RSParser
import RSWeb
import SyncDatabase
import os.log
import Secrets
public enum ReaderAPIAccountDelegateError: LocalizedError {
@ -34,14 +33,13 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
}
}
final class ReaderAPIAccountDelegate: AccountDelegate {
final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
private let variant: ReaderAPIVariant
private let database: SyncDatabase
private let caller: ReaderAPICaller
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ReaderAPI")
var behaviors: AccountBehaviors {
var behaviors: AccountBehaviors = [.disallowOPMLImports, .disallowFeedInMultipleFolders]
@ -196,7 +194,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Sending article statuses...")
logger.debug("Sending article statuses...")
database.selectForProcessing { result in
@ -229,7 +227,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
self.logger.debug("Done sending article statuses.")
completion(.success(()))
}
}
@ -244,7 +242,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Refreshing article statuses...")
logger.debug("Refreshing article statuses...")
let group = DispatchGroup()
var errorOccurred = false
@ -258,7 +256,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
case .failure(let error):
errorOccurred = true
os_log(.info, log: self.log, "Retrieving unread entries failed: %@.", error.localizedDescription)
self.logger.info("Retrieving unread entries failed: \(error.localizedDescription)")
group.leave()
}
@ -273,14 +271,14 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
case .failure(let error):
errorOccurred = true
os_log(.info, log: self.log, "Retrieving starred entries failed: %@.", error.localizedDescription)
self.logger.info("Retrieving starred entries failed: \(error.localizedDescription)")
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done refreshing article statuses.")
self.logger.debug("Done refreshing article statuses.")
if errorOccurred {
completion(.failure(ReaderAPIAccountDelegateError.unknown))
} else {
@ -342,7 +340,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
}
case .failure(let error):
os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
self.logger.error("Remove feed error: \(error.localizedDescription)")
}
}
}
@ -361,7 +359,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
account.clearWebFeedMetadata(feed)
}
case .failure(let error):
os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
self.logger.error("Remove feed error: \(error.localizedDescription)")
}
}
@ -593,7 +591,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
case .success:
break
case .failure(let error):
os_log(.error, log: self.log, "Restore folder feed error: %@.", error.localizedDescription)
self.logger.error("Restore folder feed error: \(error.localizedDescription)")
}
}
@ -711,7 +709,7 @@ private extension ReaderAPIAccountDelegate {
guard !folderTags.isEmpty else { return }
os_log(.debug, log: log, "Syncing folders with %ld tags.", folderTags.count)
logger.debug("Syncing folders with \(folderTags.count) tags.")
let readerFolderExternalIDs = folderTags.compactMap { $0.tagID }
@ -751,7 +749,7 @@ private extension ReaderAPIAccountDelegate {
guard let subscriptions = subscriptions else { return }
assert(Thread.isMainThread)
os_log(.debug, log: log, "Syncing feeds with %ld subscriptions.", subscriptions.count)
logger.debug("Syncing feeds with \(subscriptions.count) subscriptions")
let subFeedIds = subscriptions.map { $0.feedID }
@ -793,7 +791,7 @@ private extension ReaderAPIAccountDelegate {
func syncFeedFolderRelationship(_ account: Account, _ subscriptions: [ReaderAPISubscription]?) {
guard let subscriptions = subscriptions else { return }
assert(Thread.isMainThread)
os_log(.debug, log: log, "Syncing taggings with %ld subscriptions.", subscriptions.count)
logger.debug("Syncing taggins with \(subscriptions.count) subscriptions.")
// Set up some structures to make syncing easier
let folderDict = externalIDToFolderDictionary(with: account.folders)
@ -887,7 +885,7 @@ private extension ReaderAPIAccountDelegate {
self.database.deleteSelectedForProcessing(articleIDGroup.map { $0 } )
group.leave()
case .failure(let error):
os_log(.error, log: self.log, "Article status sync call failed: %@.", error.localizedDescription)
self.logger.error("Article status sync call failed: \(error.localizedDescription)")
self.database.resetSelectedForProcessing(articleIDGroup.map { $0 } )
group.leave()
}
@ -987,7 +985,7 @@ private extension ReaderAPIAccountDelegate {
return
}
os_log(.debug, log: self.log, "Refreshing missing articles...")
self.logger.debug("Refreshing missing articles...")
let group = DispatchGroup()
let articleIDs = Array(fetchedArticleIDs)
@ -1007,7 +1005,7 @@ private extension ReaderAPIAccountDelegate {
}
case .failure(let error):
os_log(.error, log: self.log, "Refresh missing articles failed: %@.", error.localizedDescription)
self.logger.error("Refresh missing articles failed: \(error.localizedDescription)")
group.leave()
}
}
@ -1015,7 +1013,7 @@ private extension ReaderAPIAccountDelegate {
group.notify(queue: DispatchQueue.main) {
self.refreshProgress.completeTask()
os_log(.debug, log: self.log, "Done refreshing missing articles.")
self.logger.debug("Done refreshing missing articles.")
completion()
}
}
@ -1121,7 +1119,7 @@ private extension ReaderAPIAccountDelegate {
case .success(let pendingArticleIDs):
process(pendingArticleIDs)
case .failure(let error):
os_log(.error, log: self.log, "Sync Article Read Status failed: %@.", error.localizedDescription)
self.logger.error("Sync Article Read Status failed: \(error.localizedDescription)")
}
}
@ -1170,7 +1168,7 @@ private extension ReaderAPIAccountDelegate {
case .success(let pendingArticleIDs):
process(pendingArticleIDs)
case .failure(let error):
os_log(.error, log: self.log, "Sync Article Starred Status failed: %@.", error.localizedDescription)
self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription)")
}
}

View File

@ -7,12 +7,9 @@
//
import Foundation
import os.log
import RSCore
final class WebFeedMetadataFile {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "webFeedMetadataFile")
final class WebFeedMetadataFile: Logging {
private let fileURL: URL
private let account: Account
@ -52,8 +49,8 @@ final class WebFeedMetadataFile {
do {
let data = try encoder.encode(feedMetadata)
try data.write(to: fileURL)
} catch let error as NSError {
os_log(.error, log: log, "Save to disk failed: %@.", error.localizedDescription)
} catch let error {
logger.error("Save to disk failed: \(error.localizedDescription)")
}
}

View File

@ -3,7 +3,7 @@ import PackageDescription
let package = Package(
name: "Articles",
platforms: [.macOS(SupportedPlatform.MacOSVersion.v10_15), .iOS(SupportedPlatform.IOSVersion.v13)],
platforms: [.macOS(SupportedPlatform.MacOSVersion.v11), .iOS(SupportedPlatform.IOSVersion.v14)],
products: [
.library(
name: "Articles",

View File

@ -21,7 +21,7 @@ dependencies.append(contentsOf: [
let package = Package(
name: "ArticlesDatabase",
platforms: [.macOS(SupportedPlatform.MacOSVersion.v10_15), .iOS(SupportedPlatform.IOSVersion.v13)],
platforms: [.macOS(SupportedPlatform.MacOSVersion.v11), .iOS(SupportedPlatform.IOSVersion.v14)],
products: [
.library(
name: "ArticlesDatabase",

View File

@ -15,7 +15,6 @@ import Account
import RSCore
import RSCoreResources
import Secrets
import OSLog
import CrashReporter
// If we're not going to import Sparkle, provide dummy protocols to make it easy
@ -30,15 +29,13 @@ import Sparkle
var appDelegate: AppDelegate!
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate, Logging
{
private struct WindowRestorationIdentifiers {
static let mainWindow = "mainWindow"
}
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
var userNotificationManager: UserNotificationManager!
var faviconDownloader: FaviconDownloader!
var imageDownloader: ImageDownloader!
@ -194,14 +191,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
try self.softwareUpdater.start()
}
catch {
NSLog("Failed to start software updater with error: \(error)")
logger.error("Failed to start software updater with error: \(error, privacy: .public)")
}
#endif
AppDefaults.shared.registerDefaults()
let isFirstRun = AppDefaults.shared.isFirstRun
if isFirstRun {
os_log(.debug, log: log, "Is first run.")
logger.debug("Is first run")
}
let localAccount = AccountManager.shared.defaultAccount
@ -856,6 +853,7 @@ internal extension AppDelegate {
confirmImportSuccess(themeName: theme.name)
} catch {
NSApplication.shared.presentError(error)
logger.error("Error importing theme: \(error.localizedDescription, privacy: .public)")
}
}
@ -884,6 +882,7 @@ internal extension AppDelegate {
}
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error" : error, "path": filename])
logger.error("Error importing theme: \(error.localizedDescription, privacy: .public)")
}
}
@ -1012,12 +1011,12 @@ private extension AppDelegate {
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, log: log, "No account found from notification.")
logger.debug("No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, log: log, "No article found from search using %@", articleID)
logger.debug("No article found from search using: \(articleID, privacy: .public)")
return
}
account!.markArticles(article!, statusKey: .read, flag: true) { _ in }
@ -1031,12 +1030,12 @@ private extension AppDelegate {
}
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, log: log, "No account found from notification.")
logger.debug("No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, log: log, "No article found from search using %@", articleID)
logger.debug("No article found from search using: \(articleID, privacy: .public)")
return
}
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }

View File

@ -8,18 +8,18 @@
import AppKit
import Account
import os.log
import RSCore
struct ErrorHandler {
struct ErrorHandler: Logging {
private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Account")
public static func present(_ error: Error) {
NSApplication.shared.presentError(error)
}
public static func log(_ error: Error) {
os_log(.error, log: self.log, "%@", error.localizedDescription)
ErrorHandler.logger.error("\(error.localizedDescription, privacy: .public)")
}
}

View File

@ -10,7 +10,6 @@ import Foundation
import RSCore
import Articles
import Account
import os.log
protocol TimelineDelegate: AnyObject {
func timelineSelectionDidChange(_: TimelineViewController, selectedArticles: [Article]?)

View File

@ -8,10 +8,11 @@
import AppKit
import Account
import RSCore
import RSWeb
import Secrets
class AccountsFeedbinWindowController: NSWindowController {
class AccountsFeedbinWindowController: NSWindowController, Logging {
@IBOutlet weak var signInTextField: NSTextField!
@IBOutlet weak var noAccountTextField: NSTextField!
@ -115,6 +116,7 @@ class AccountsFeedbinWindowController: NSWindowController {
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)")
}
case .failure:

View File

@ -9,9 +9,10 @@
import AppKit
import Account
import RSWeb
import RSCore
import Secrets
class AccountsNewsBlurWindowController: NSWindowController {
class AccountsNewsBlurWindowController: NSWindowController, Logging {
@IBOutlet weak var signInTextField: NSTextField!
@IBOutlet weak var noAccountTextField: NSTextField!
@ -113,6 +114,7 @@ class AccountsNewsBlurWindowController: NSWindowController {
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)")
}
case .failure:

View File

@ -9,9 +9,10 @@
import AppKit
import Account
import RSWeb
import RSCore
import Secrets
class AccountsReaderAPIWindowController: NSWindowController {
class AccountsReaderAPIWindowController: NSWindowController, Logging {
@IBOutlet weak var titleImageView: NSImageView!
@IBOutlet weak var titleLabel: NSTextField!
@ -170,6 +171,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public)")
}
case .failure:

View File

@ -55,7 +55,7 @@ extension AppDelegate : AppDelegateAppleEvents {
if let themeURL = URL(string: themeURLString) {
let request = URLRequest(url: themeURL)
let task = URLSession.shared.downloadTask(with: request) { location, response, error in
let task = URLSession.shared.downloadTask(with: request) { [weak self] location, response, error in
guard let location = location else {
return
}
@ -64,6 +64,7 @@ extension AppDelegate : AppDelegateAppleEvents {
try ArticleThemeDownloader.shared.handleFile(at: location)
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
self?.logger.error("Failed to import theme: \(error.localizedDescription, privacy: .public)")
}
}
task.resume()

View File

@ -7,7 +7,6 @@
//
import Cocoa
import os.log
class ShareViewController: NSViewController {
@ -16,7 +15,6 @@ class ShareViewController: NSViewController {
private var url: URL?
private var extensionContainers: ExtensionContainers?
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ShareViewController")
override var nibName: NSNib.Name? {
return NSNib.Name("ShareViewController")

View File

@ -847,6 +847,8 @@
D5F4EDB720074D6500B9E363 /* WebFeed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* WebFeed+Scriptability.swift */; };
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
DD82AB0A231003F6002269DF /* SharingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD82AB09231003F6002269DF /* SharingTests.swift */; };
DF2A8F33289BFBD9002455AD /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = DF2A8F32289BFBD9002455AD /* RSCore */; };
DF2A8F34289BFBDA002455AD /* RSCore in Embed Frameworks */ = {isa = PBXBuildFile; productRef = DF2A8F32289BFBD9002455AD /* RSCore */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
DFD6AACF27ADE86E00463FAD /* NewsFax.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = DFD6AACD27ADE86E00463FAD /* NewsFax.nnwtheme */; };
DFFB8FC2279B75E300AC21D7 /* Account in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51BC2F4A24D343A500E90810 /* Account */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
DFFC199827A0D0D7004B7AEF /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */; };
@ -930,6 +932,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
DF2A8F34289BFBDA002455AD /* RSCore in Embed Frameworks */,
17EF6A2225C4E5B4002C9F81 /* RSWeb in Embed Frameworks */,
);
name = "Embed Frameworks";
@ -1599,6 +1602,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DF2A8F33289BFBD9002455AD /* RSCore in Frameworks */,
17EF6A2125C4E5B4002C9F81 /* RSWeb in Frameworks */,
176813F72564BB2C00D98635 /* SwiftUI.framework in Frameworks */,
176813F52564BB2C00D98635 /* WidgetKit.framework in Frameworks */,
@ -2583,6 +2587,7 @@
849A97861ED9ECEF007D329B /* Article Styles */,
84DAEE201F86CAE00058304B /* Importers */,
8444C9011FED81880051386C /* Exporters */,
DF2A8F28289A3EA8002455AD /* Logging */,
51FE0FF9234552490056195D /* UserNotifications */,
84F2D5341FC22FCB00998D64 /* SmartFeeds */,
51B5C85A23F22A7A00032075 /* ShareExtension */,
@ -2862,6 +2867,13 @@
path = Scriptability;
sourceTree = "<group>";
};
DF2A8F28289A3EA8002455AD /* Logging */ = {
isa = PBXGroup;
children = (
);
path = Logging;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -2882,6 +2894,7 @@
name = "NetNewsWire iOS Widget Extension";
packageProductDependencies = (
17EF6A2025C4E5B4002C9F81 /* RSWeb */,
DF2A8F32289BFBD9002455AD /* RSCore */,
);
productName = "NetNewsWire WidgetExtension";
productReference = 176813F32564BB2C00D98635 /* NetNewsWire iOS Widget Extension.appex */;
@ -5181,6 +5194,11 @@
package = 653813412680E2DA007A082C /* XCRemoteSwiftPackageReference "RSCore" */;
productName = RSCore;
};
DF2A8F32289BFBD9002455AD /* RSCore */ = {
isa = XCSwiftPackageProductDependency;
package = 5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */;
productName = RSCore;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 849C64581ED37A5D003D8FC0 /* Project object */;

View File

@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/microsoft/plcrashreporter.git",
"state": {
"branch": null,
"revision": "6b27393cad517c067dceea85fadf050e70c4ceaa",
"version": "1.10.1"
"revision": "81cdec2b3827feb03286cb297f4c501a8eb98df1",
"version": "1.10.2"
}
},
{
@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/Ranchero-Software/RSCore.git",
"state": {
"branch": null,
"revision": "4cd6b67ed213c0db9c02c9d8ff968b45fd694970",
"version": "1.0.8"
"revision": "814adfd956e5bd099b3b81b6680d0a4f6cd56130",
"version": "1.0.9"
}
},
{

View File

@ -3,7 +3,7 @@ import PackageDescription
let package = Package(
name: "Secrets",
platforms: [.macOS(SupportedPlatform.MacOSVersion.v10_15), .iOS(SupportedPlatform.IOSVersion.v13)],
platforms: [.macOS(SupportedPlatform.MacOSVersion.v11), .iOS(SupportedPlatform.IOSVersion.v14)],
products: [
.library(
name: "Secrets",

View File

@ -9,6 +9,7 @@
import Foundation
import Account
import Secrets
import RSCore
public enum ArticleExtractorState {
case ready
@ -23,7 +24,7 @@ protocol ArticleExtractorDelegate {
func articleExtractionDidComplete(extractedArticle: ExtractedArticle)
}
class ArticleExtractor {
class ArticleExtractor: Logging {
private var dataTask: URLSessionDataTask? = nil
@ -91,6 +92,7 @@ class ArticleExtractor {
}
}
} catch {
self.logger.error("Failed to extract article: \(error.localizedDescription, privacy: .public)")
self.state = .failedToParse
DispatchQueue.main.async {
self.delegate?.articleExtractionDidFail(with: error)

View File

@ -8,8 +8,9 @@
import Foundation
import Zip
import RSCore
public class ArticleThemeDownloader {
public class ArticleThemeDownloader: Logging {
public enum ArticleThemeDownloaderError: LocalizedError {
case noThemeFile
@ -63,6 +64,7 @@ public class ArticleThemeDownloader {
}
return URL(fileURLWithPath: unzipDirectory.appendingPathComponent(themeFilePath!).path)
} catch {
logger.error("Failed to unzip theme: \(error.localizedDescription, privacy: .public)")
try? FileManager.default.removeItem(at: location)
throw error
}
@ -101,7 +103,7 @@ public class ArticleThemeDownloader {
try FileManager.default.removeItem(atPath: downloadDirectory().appendingPathComponent(path).path)
}
} catch {
print(error)
logger.error("Failed to clean up theme download: \(error.localizedDescription, privacy: .public)")
}
}
}

View File

@ -14,7 +14,7 @@ public extension Notification.Name {
static let CurrentArticleThemeDidChangeNotification = Notification.Name("CurrentArticleThemeDidChangeNotification")
}
final class ArticleThemesManager: NSObject, NSFilePresenter {
final class ArticleThemesManager: NSObject, NSFilePresenter, Logging {
static var shared: ArticleThemesManager!
public let folderPath: String
@ -58,6 +58,7 @@ final class ArticleThemesManager: NSObject, NSFilePresenter {
do {
try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
} catch {
logger.error("Could not create folder for themes: \(error.localizedDescription, privacy: .public)")
assertionFailure("Could not create folder for Themes.")
abort()
}
@ -113,6 +114,7 @@ final class ArticleThemesManager: NSObject, NSFilePresenter {
return try ArticleTheme(path: path, isAppTheme: isAppTheme)
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
logger.error("Failed to import theme: \(error.localizedDescription, privacy: .public)")
return nil
}

View File

@ -7,12 +7,10 @@
//
import Foundation
import os.log
import RSWeb
import RSCore
struct CacheCleaner {
static let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CacheCleaner")
struct CacheCleaner: Logging {
static func purgeIfNecessary() {
@ -35,10 +33,10 @@ struct CacheCleaner {
for tempItem in [faviconsFolderURL, imagesFolderURL, feedURLToIconURL, homePageToIconURL, homePagesWithNoIconURL] {
do {
os_log(.info, log: self.log, "Removing cache file: %@", tempItem.absoluteString)
CacheCleaner.logger.info("Removing cache file: \(tempItem.absoluteString, privacy: .public)")
try FileManager.default.removeItem(at: tempItem)
} catch {
os_log(.error, log: self.log, "Could not delete cache file: %@", error.localizedDescription)
CacheCleaner.logger.error("Could not delete cache file: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -18,7 +18,7 @@ extension Notification.Name {
static let FaviconDidBecomeAvailable = Notification.Name("FaviconDidBecomeAvailableNotification") // userInfo key: FaviconDownloader.UserInfoKey.faviconURL
}
final class FaviconDownloader {
final class FaviconDownloader: Logging {
private static let saveQueue = CoalescingQueue(name: "Cache Save Queue", interval: 1.0)
@ -297,6 +297,7 @@ private extension FaviconDownloader {
let data = try encoder.encode(homePageToFaviconURLCache)
try data.write(to: url)
} catch {
logger.error("Failed to Save Home Page To Favicon URL Cache: \(error.localizedDescription, privacy: .public)")
assertionFailure(error.localizedDescription)
}
}
@ -311,6 +312,7 @@ private extension FaviconDownloader {
let data = try encoder.encode(Array(homePageURLsWithNoFaviconURLCache))
try data.write(to: url)
} catch {
logger.error("Failed to Save URLs With No Favicon URL Cache: \(error.localizedDescription, privacy: .public)")
assertionFailure(error.localizedDescription)
}
}

View File

@ -7,7 +7,6 @@
//
import Foundation
import os.log
import RSCore
import RSWeb
@ -19,7 +18,7 @@ extension Notification.Name {
static let DidLoadFavicon = Notification.Name("DidLoadFaviconNotification")
}
final class SingleFaviconDownloader {
final class SingleFaviconDownloader: Logging {
enum DiskStatus {
case unknown, notOnDisk, onDisk
@ -29,8 +28,6 @@ final class SingleFaviconDownloader {
var iconImage: IconImage?
let homePageURL: String?
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "SingleFaviconDownloader")
private var lastDownloadAttemptDate: Date
private var diskStatus = DiskStatus.unknown
private var diskCache: BinaryDiskCache
@ -128,7 +125,9 @@ private extension SingleFaviconDownloader {
self.diskStatus = .onDisk
}
}
catch {}
catch {
self.logger.error("Unable to save to disk: \(error.localizedDescription, privacy: .public)")
}
}
}
@ -139,16 +138,16 @@ private extension SingleFaviconDownloader {
return
}
downloadUsingCache(url) { (data, response, error) in
downloadUsingCache(url) { [weak self] (data, response, error) in
if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil {
self.saveToDisk(data)
self?.saveToDisk(data)
RSImage.image(with: data, imageResultBlock: completion)
return
}
if let error = error {
os_log(.info, log: self.log, "Error downloading image at %@: %@.", url.absoluteString, error.localizedDescription)
self?.logger.error("Error downloading image at: \(url.absoluteString, privacy: .sensitive): \(error.localizedDescription, privacy: .public)")
}
completion(nil)

View File

@ -7,7 +7,6 @@
//
import Foundation
import os.log
import RSCore
import RSWeb
@ -16,9 +15,7 @@ extension Notification.Name {
static let ImageDidBecomeAvailable = Notification.Name("ImageDidBecomeAvailableNotification") // UserInfoKey.url
}
final class ImageDownloader {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ImageDownloader")
final class ImageDownloader: Logging {
private let folder: String
private var diskCache: BinaryDiskCache
@ -103,19 +100,19 @@ private extension ImageDownloader {
return
}
downloadUsingCache(imageURL) { (data, response, error) in
downloadUsingCache(imageURL) { [weak self] (data, response, error) in
if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil {
self.saveToDisk(url, data)
self?.saveToDisk(url, data)
completion(data)
return
}
if let response = response as? HTTPURLResponse, response.statusCode >= HTTPResponseCode.badRequest && response.statusCode <= HTTPResponseCode.notAcceptable {
self.badURLs.insert(url)
self?.badURLs.insert(url)
}
if let error = error {
os_log(.info, log: self.log, "Error downloading image at %@: %@.", url, error.localizedDescription)
self?.logger.error("Error downloading image at: \(url, privacy: .sensitive): \(error.localizedDescription, privacy: .public)")
}
completion(nil)

View File

@ -7,15 +7,12 @@
//
import Foundation
import os.log
import RSCore
import RSParser
import Account
final class ExtensionContainersFile {
final class ExtensionContainersFile: Logging {
private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "extensionContainersFile")
private static var filePath: String = {
let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
@ -55,7 +52,7 @@ final class ExtensionContainersFile {
})
if let error = errorPointer?.pointee {
os_log(.error, log: log, "Read from disk coordination failed: %@.", error.localizedDescription)
logger.error("Read from coordination failed: \(error.localizedDescription, privacy: .public)")
}
return extensionContainers
@ -88,19 +85,19 @@ private extension ExtensionContainersFile {
let fileCoordinator = NSFileCoordinator()
let fileURL = URL(fileURLWithPath: ExtensionContainersFile.filePath)
fileCoordinator.coordinate(writingItemAt: fileURL, options: [], error: errorPointer, byAccessor: { writeURL in
fileCoordinator.coordinate(writingItemAt: fileURL, options: [], error: errorPointer, byAccessor: { [weak self] writeURL in
do {
let extensionAccounts = AccountManager.shared.sortedActiveAccounts.map { ExtensionAccount(account: $0) }
let extensionContainers = ExtensionContainers(accounts: extensionAccounts)
let data = try encoder.encode(extensionContainers)
try data.write(to: writeURL)
} catch let error as NSError {
os_log(.error, log: Self.log, "Save to disk failed: %@.", error.localizedDescription)
self?.logger.error("Save to disk failed: \(error.localizedDescription, privacy: .public)")
}
})
if let error = errorPointer?.pointee {
os_log(.error, log: Self.log, "Save to disk coordination failed: %@.", error.localizedDescription)
logger.error("Save to disk coordination failed: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -7,13 +7,11 @@
//
import Foundation
import os.log
import Account
import RSCore
final class ExtensionFeedAddRequestFile: NSObject, NSFilePresenter {
final class ExtensionFeedAddRequestFile: NSObject, NSFilePresenter, Logging {
private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "extensionFeedAddRequestFile")
private static var filePath: String = {
let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
@ -82,12 +80,12 @@ final class ExtensionFeedAddRequestFile: NSObject, NSFilePresenter {
try data.write(to: url)
} catch let error as NSError {
os_log(.error, log: Self.log, "Save to disk failed: %@.", error.localizedDescription)
logger.error("Save to disk failed: \(error.localizedDescription, privacy: .public)")
}
})
if let error = errorPointer?.pointee {
os_log(.error, log: Self.log, "Save to disk coordination failed: %@.", error.localizedDescription)
logger.error("Save to disk coordination failed: \(error.localizedDescription, privacy: .public)")
}
}
@ -107,7 +105,7 @@ private extension ExtensionFeedAddRequestFile {
var requests: [ExtensionFeedAddRequest]? = nil
fileCoordinator.coordinate(writingItemAt: fileURL, options: [.forMerging], error: errorPointer, byAccessor: { url in
fileCoordinator.coordinate(writingItemAt: fileURL, options: [.forMerging], error: errorPointer, byAccessor: { [weak self] url in
do {
if let fileData = try? Data(contentsOf: url),
@ -119,12 +117,12 @@ private extension ExtensionFeedAddRequestFile {
try data.write(to: url)
} catch let error as NSError {
os_log(.error, log: Self.log, "Save to disk failed: %@.", error.localizedDescription)
self?.logger.error("Save to disk failed: \(error.localizedDescription, privacy: .public)")
}
})
if let error = errorPointer?.pointee {
os_log(.error, log: Self.log, "Save to disk coordination failed: %@.", error.localizedDescription)
logger.error("Save to disk coordination failed: \(error.localizedDescription, privacy: .public)")
}
requests?.forEach { processRequest($0) }

View File

@ -7,10 +7,11 @@
//
import Foundation
import RSCore
struct WidgetDataDecoder {
struct WidgetDataDecoder: Logging {
static func decodeWidgetData() throws -> WidgetData {
func decodeWidgetData() throws -> WidgetData {
let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
let dataURL = containerURL?.appendingPathComponent("widget-data.json")
@ -22,13 +23,14 @@ struct WidgetDataDecoder {
}
}
static func sampleData() -> WidgetData {
func sampleData() -> WidgetData {
let pathToSample = Bundle.main.url(forResource: "widget-sample", withExtension: "json")
do {
let data = try Data(contentsOf: pathToSample!)
let decoded = try JSONDecoder().decode(WidgetData.self, from: data)
return decoded
} catch {
logger.error("Error accessing sample widget data: \(error.localizedDescription, privacy: .public)")
return WidgetData(currentUnreadCount: 0, currentTodayCount: 0, currentStarredCount: 0, unreadArticles: [], starredArticles: [], todayArticles: [], lastUpdateTime: Date())
}
}

View File

@ -8,16 +8,15 @@
import Foundation
import WidgetKit
import os.log
import UIKit
import RSCore
import Articles
import Account
public final class WidgetDataEncoder {
public final class WidgetDataEncoder: Logging {
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
private let fetchLimit = 7
private var backgroundTaskID: UIBackgroundTaskIdentifier!
@ -36,7 +35,7 @@ public final class WidgetDataEncoder {
@available(iOS 14, *)
func encodeWidgetData() throws {
flushSharedContainer()
os_log(.debug, log: log, "Starting encoding widget data.")
logger.debug("Started encoding widget data.")
do {
let unreadArticles = Array(try AccountManager.shared.fetchArticles(.unread(fetchLimit))).sortedByDate(.orderedDescending)
@ -95,14 +94,14 @@ public final class WidgetDataEncoder {
}
let encodedData = try? JSONEncoder().encode(latestData)
os_log(.debug, log: self.log, "Finished encoding widget data.")
self.logger.debug("Finished encoding widget data.")
if self.fileExists() {
try? FileManager.default.removeItem(at: self.dataURL!)
os_log(.debug, log: self.log, "Removed widget data from container.")
self.logger.debug("Removed widget data from container.")
}
if FileManager.default.createFile(atPath: self.dataURL!.path, contents: encodedData, attributes: nil) {
os_log(.debug, log: self.log, "Wrote widget data to container.")
self.logger.debug("Wrote widget data to container.")
WidgetCenter.shared.reloadAllTimelines()
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
self.backgroundTaskID = .invalid

View File

@ -18,7 +18,7 @@ dependencies.append(contentsOf: [
let package = Package(
name: "SyncDatabase",
platforms: [.macOS(SupportedPlatform.MacOSVersion.v10_15), .iOS(SupportedPlatform.IOSVersion.v13)],
platforms: [.macOS(.v11), .iOS(.v14)],
products: [
.library(
name: "SyncDatabase",

View File

@ -8,35 +8,41 @@
import WidgetKit
import SwiftUI
import RSCore
struct Provider: TimelineProvider {
struct Provider: TimelineProvider, Logging {
let decoder = WidgetDataDecoder()
func placeholder(in context: Context) -> WidgetTimelineEntry {
do {
let data = try WidgetDataDecoder.decodeWidgetData()
let data = try decoder.decodeWidgetData()
return WidgetTimelineEntry(date: Date(), widgetData: data)
} catch {
return WidgetTimelineEntry(date: Date(), widgetData: WidgetDataDecoder.sampleData())
logger.error("Failed to decode widget data: \(error.localizedDescription, privacy: .public)")
return WidgetTimelineEntry(date: Date(), widgetData: decoder.sampleData())
}
}
func getSnapshot(in context: Context, completion: @escaping (WidgetTimelineEntry) -> Void) {
if context.isPreview {
do {
let data = try WidgetDataDecoder.decodeWidgetData()
let data = try decoder.decodeWidgetData()
completion(WidgetTimelineEntry(date: Date(), widgetData: data))
} catch {
logger.error("Failed to decode widget data: \(error.localizedDescription, privacy: .public)")
completion(WidgetTimelineEntry(date: Date(),
widgetData: WidgetDataDecoder.sampleData()))
widgetData: decoder.sampleData()))
}
} else {
do {
let widgetData = try WidgetDataDecoder.decodeWidgetData()
let widgetData = try decoder.decodeWidgetData()
let entry = WidgetTimelineEntry(date: Date(), widgetData: widgetData)
completion(entry)
} catch {
logger.error("Failed to decode widget data: \(error.localizedDescription, privacy: .public)")
let entry = WidgetTimelineEntry(date: Date(),
widgetData: WidgetDataDecoder.sampleData())
widgetData: decoder.sampleData())
completion(entry)
}
}
@ -48,9 +54,10 @@ struct Provider: TimelineProvider {
var entry: WidgetTimelineEntry
do {
let widgetData = try WidgetDataDecoder.decodeWidgetData()
let widgetData = try decoder.decodeWidgetData()
entry = WidgetTimelineEntry(date: date, widgetData: widgetData)
} catch {
logger.error("Failed to decode widget data: \(error.localizedDescription, privacy: .public)")
entry = WidgetTimelineEntry(date: date, widgetData: WidgetData(currentUnreadCount: 0, currentTodayCount: 0, currentStarredCount: 0, unreadArticles: [], starredArticles: [], todayArticles: [], lastUpdateTime: Date()))
}

View File

@ -102,6 +102,6 @@ struct SmartFeedSummaryWidgetView: View {
struct SmartFeedSummaryWidgetView_Previews: PreviewProvider {
static var previews: some View {
SmartFeedSummaryWidgetView(entry: Provider.Entry.init(date: Date(), widgetData: WidgetDataDecoder.sampleData()))
SmartFeedSummaryWidgetView(entry: Provider.Entry.init(date: Date(), widgetData: WidgetDataDecoder().sampleData()))
}
}

View File

@ -11,8 +11,9 @@ import Account
import Secrets
import RSWeb
import SafariServices
import RSCore
class FeedbinAccountViewController: UITableViewController {
class FeedbinAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@ -116,7 +117,9 @@ class FeedbinAccountViewController: UITableViewController {
do {
try self.account?.removeCredentials(type: .basic)
} catch {}
} catch {
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
}
try self.account?.storeCredentials(credentials)
self.account?.refreshAll() { result in
@ -132,6 +135,7 @@ class FeedbinAccountViewController: UITableViewController {
self.delegate?.dismiss()
} catch {
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
}
} else {
self.showError(NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error"))

View File

@ -10,9 +10,10 @@ import UIKit
import Account
import Secrets
import RSWeb
import RSCore
import SafariServices
class NewsBlurAccountViewController: UITableViewController {
class NewsBlurAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@ -118,7 +119,9 @@ class NewsBlurAccountViewController: UITableViewController {
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
} catch {}
} catch {
self.logger.error("Error removing credentials: \(error.localizedDescription, privacy: .public).")
}
try self.account?.storeCredentials(basicCredentials)
try self.account?.storeCredentials(sessionCredentials)
@ -135,6 +138,7 @@ class NewsBlurAccountViewController: UITableViewController {
self.delegate?.dismiss()
} catch {
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
}
} else {
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))

View File

@ -11,8 +11,9 @@ import Account
import Secrets
import RSWeb
import SafariServices
import RSCore
class ReaderAPIAccountViewController: UITableViewController {
class ReaderAPIAccountViewController: UITableViewController, Logging {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
@ -187,6 +188,7 @@ class ReaderAPIAccountViewController: UITableViewController {
self.delegate?.dismiss()
} catch {
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
self.logger.error("Keychain error while storing credentials: \(error.localizedDescription, privacy: .public).")
}
} else {
self.showError(NSLocalizedString("Invalid username/password combination.", comment: "Credentials Error"))

View File

@ -11,14 +11,13 @@ import RSCore
import RSWeb
import Account
import BackgroundTasks
import os.log
import Secrets
import WidgetKit
var appDelegate: AppDelegate!
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, UnreadCountProvider {
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, UnreadCountProvider, Logging {
private var bgTaskDispatchQueue = DispatchQueue.init(label: "BGTaskScheduler")
@ -36,8 +35,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
var userNotificationManager: UserNotificationManager!
var faviconDownloader: FaviconDownloader!
var imageDownloader: ImageDownloader!
@ -83,7 +80,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
let isFirstRun = AppDefaults.shared.isFirstRun
if isFirstRun {
os_log("Is first run.", log: log, type: .info)
logger.info("Is first run.")
}
if isFirstRun && !AccountManager.shared.anyAccountHasAtLeastOneFeed() {
@ -166,7 +163,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func resumeDatabaseProcessingIfNecessary() {
if AccountManager.shared.isSuspended {
AccountManager.shared.resumeAll()
os_log("Application processing resumed.", log: self.log, type: .info)
logger.info("Application processing resumed.")
}
}
@ -276,7 +273,7 @@ private extension AppDelegate {
self.waitBackgroundUpdateTask = UIApplication.shared.beginBackgroundTask { [weak self] in
guard let self = self else { return }
self.completeProcessing(true)
os_log("Accounts wait for progress terminated for running too long.", log: self.log, type: .info)
self.logger.info("Accounts wait for progress terminated for running too long.")
}
DispatchQueue.main.async { [weak self] in
@ -288,18 +285,18 @@ private extension AppDelegate {
func waitToComplete(completion: @escaping (Bool) -> Void) {
guard UIApplication.shared.applicationState == .background else {
os_log("App came back to foreground, no longer waiting.", log: self.log, type: .info)
logger.info("App came back to foreground, no longer waiting.")
completion(false)
return
}
if AccountManager.shared.refreshInProgress || isSyncArticleStatusRunning {
os_log("Waiting for sync to finish...", log: self.log, type: .info)
logger.info("Waiting for sync to finish...")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
self?.waitToComplete(completion: completion)
}
} else {
os_log("Refresh progress complete.", log: self.log, type: .info)
logger.info("Refresh progress complete.")
completion(true)
}
}
@ -324,9 +321,9 @@ private extension AppDelegate {
self.syncBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
}
self.syncBackgroundUpdateTask = UIApplication.shared.beginBackgroundTask {
self.syncBackgroundUpdateTask = UIApplication.shared.beginBackgroundTask { [weak self] in
completeProcessing()
os_log("Accounts sync processing terminated for running too long.", log: self.log, type: .info)
self?.logger.info("Accounts sync processing terminated for running too long.")
}
DispatchQueue.main.async {
@ -350,7 +347,7 @@ private extension AppDelegate {
}
}
os_log("Application processing suspended.", log: self.log, type: .info)
logger.info("Application processing suspended.")
}
}
@ -374,11 +371,11 @@ private extension AppDelegate {
// We send this to a dedicated serial queue because as of 11/05/19 on iOS 13.2 the call to the
// task scheduler can hang indefinitely.
bgTaskDispatchQueue.async {
bgTaskDispatchQueue.async { [weak self] in
do {
try BGTaskScheduler.shared.submit(request)
} catch {
os_log(.error, log: self.log, "Could not schedule app refresh: %@", error.localizedDescription)
self?.logger.error("Could not schedule app refresh: \(error.localizedDescription, privacy: .public)")
}
}
}
@ -390,7 +387,7 @@ private extension AppDelegate {
scheduleBackgroundFeedRefresh() // schedule next refresh
os_log("Woken to perform account refresh.", log: self.log, type: .info)
logger.info("Woken to perform account refresh.")
DispatchQueue.main.async {
if AccountManager.shared.isSuspended {
@ -400,7 +397,7 @@ private extension AppDelegate {
if !AccountManager.shared.isSuspended {
try? WidgetDataEncoder.shared.encodeWidgetData()
self.suspendApplication()
os_log("Account refresh operation completed.", log: self.log, type: .info)
self.logger.info("Account refresh operation completed.")
task.setTaskCompleted(success: true)
}
}
@ -408,7 +405,7 @@ private extension AppDelegate {
// set expiration handler
task.expirationHandler = { [weak task] in
os_log("Accounts refresh processing terminated for running too long.", log: self.log, type: .info)
self.logger.info("Accounts refresh processing terminated for running too long.")
DispatchQueue.main.async {
self.suspendApplication()
task?.setTaskCompleted(success: false)
@ -431,12 +428,12 @@ private extension AppDelegate {
resumeDatabaseProcessingIfNecessary()
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, log: self.log, "No account found from notification.")
logger.debug("No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, log: self.log, "No article found from search using %@", articleID)
logger.debug("No account found from search using \(articleID, privacy: .public)")
return
}
account!.markArticles(article!, statusKey: .read, flag: true) { _ in }
@ -459,12 +456,12 @@ private extension AppDelegate {
resumeDatabaseProcessingIfNecessary()
let account = AccountManager.shared.existingAccount(with: accountID)
guard account != nil else {
os_log(.debug, log: self.log, "No account found from notification.")
logger.debug("No account found from notification.")
return
}
let article = try? account!.fetchArticles(.articleIDs([articleID]))
guard article != nil else {
os_log(.debug, log: self.log, "No article found from search using %@", articleID)
logger.debug("No article found from search using \(articleID, privacy: .public)")
return
}
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }

View File

@ -8,24 +8,21 @@
import UIKit
import RSCore
import os.log
struct ErrorHandler {
private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
struct ErrorHandler: Logging {
public static func present(_ viewController: UIViewController) -> (Error) -> () {
return { [weak viewController] error in
if UIApplication.shared.applicationState == .active {
viewController?.presentError(error)
} else {
ErrorHandler.log(error)
log(error)
}
}
}
public static func log(_ error: Error) {
os_log(.error, log: self.log, "%@", error.localizedDescription)
ErrorHandler.logger.error("\(error.localizedDescription, privacy: .public)")
}
}

View File

@ -50,7 +50,7 @@ struct FeedNode: Hashable {
}
}
class SceneCoordinator: NSObject, UndoableCommandRunner {
class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
var undoableCommands = [UndoableCommand]()
var undoManager: UndoManager? {
@ -1272,6 +1272,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
try ArticleThemeImporter.importTheme(controller: rootSplitViewController, filename: filename)
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error" : error])
logger.error("Failed to import theme with error: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -10,8 +10,9 @@ import UIKit
import UserNotifications
import Account
import Zip
import RSCore
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging {
var window: UIWindow?
var coordinator: SceneCoordinator!
@ -184,7 +185,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
DispatchQueue.main.async {
NotificationCenter.default.post(name: .didBeginDownloadingTheme, object: nil)
}
let task = URLSession.shared.downloadTask(with: request) { location, response, error in
let task = URLSession.shared.downloadTask(with: request) { [weak self] location, response, error in
guard
let location = location else { return }
@ -192,6 +193,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
try ArticleThemeDownloader.shared.handleFile(at: location)
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
self?.logger.error("Failed to import theme with error: \(error.localizedDescription, privacy: .public)")
}
}
task.resume()

View File

@ -7,8 +7,9 @@
//
import UIKit
import RSCore
struct ArticleThemeImporter {
struct ArticleThemeImporter: Logging {
static func importTheme(controller: UIViewController, filename: String) throws {
let theme = try ArticleTheme(path: filename, isAppTheme: false)
@ -39,6 +40,7 @@ struct ArticleThemeImporter {
confirmImportSuccess(controller: controller, themeName: theme.name)
} catch {
controller.presentError(error)
ArticleThemeImporter.logger.error("Error importing theme: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -8,10 +8,10 @@
import Foundation
import UniformTypeIdentifiers
import RSCore
import UIKit
class ArticleThemesTableViewController: UITableViewController {
class ArticleThemesTableViewController: UITableViewController, Logging {
override func viewDidLoad() {
let importBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(importTheme(_:)));
@ -118,6 +118,7 @@ extension ArticleThemesTableViewController: UIDocumentPickerDelegate {
try ArticleThemeImporter.importTheme(controller: self, filename: url.standardizedFileURL.path)
} catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error])
logger.error("Did fail to import theme: \(error.localizedDescription, privacy: .public)")
}
}

View File

@ -13,8 +13,9 @@ import SafariServices
import SwiftUI
import UniformTypeIdentifiers
import UserNotifications
import RSCore
class SettingsViewController: UITableViewController {
class SettingsViewController: UITableViewController, Logging {
private weak var opmlAccount: Account?
@ -509,6 +510,7 @@ private extension SettingsViewController {
try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
} catch {
self.presentError(title: "OPML Export Error", message: error.localizedDescription)
logger.error("OPML Export Error: \(error.localizedDescription, privacy: .public)")
}
let docPicker = UIDocumentPickerViewController(forExporting: [tempFile], asCopy: true)