diff --git a/Mac/MainWindow/NNW3/NNW3ImportController.swift b/Mac/MainWindow/NNW3/NNW3ImportController.swift index 32084490e..c54a77c76 100644 --- a/Mac/MainWindow/NNW3/NNW3ImportController.swift +++ b/Mac/MainWindow/NNW3/NNW3ImportController.swift @@ -69,7 +69,7 @@ private extension NNW3ImportController { panel.canChooseDirectories = false panel.resolvesAliases = true panel.directoryURL = NNW3ImportController.defaultFileURL - panel.allowedFileTypes = ["plist"] + panel.allowedContentTypes = [.propertyList] panel.allowsOtherFileTypes = false panel.accessoryView = accessoryViewController.view panel.isAccessoryViewDisclosed = true diff --git a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift index 4b2ce20a6..2d2ac30cf 100644 --- a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift +++ b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift @@ -8,6 +8,7 @@ import AppKit import Account +import UniformTypeIdentifiers class ExportOPMLWindowController: NSWindowController { @@ -75,7 +76,7 @@ class ExportOPMLWindowController: NSWindowController { func exportOPML(account: Account) { let panel = NSSavePanel() - panel.allowedFileTypes = ["opml"] + panel.allowedContentTypes = [UTType.opml] panel.allowsOtherFileTypes = false panel.prompt = NSLocalizedString("Export OPML", comment: "Export OPML") panel.title = NSLocalizedString("Export OPML", comment: "Export OPML") diff --git a/Mac/MainWindow/OPML/ImportOPMLWindowController.swift b/Mac/MainWindow/OPML/ImportOPMLWindowController.swift index f0737fe93..ff8843025 100644 --- a/Mac/MainWindow/OPML/ImportOPMLWindowController.swift +++ b/Mac/MainWindow/OPML/ImportOPMLWindowController.swift @@ -8,6 +8,7 @@ import AppKit import Account +import UniformTypeIdentifiers class ImportOPMLWindowController: NSWindowController { @@ -85,7 +86,7 @@ class ImportOPMLWindowController: NSWindowController { panel.allowsMultipleSelection = false panel.canChooseDirectories = false panel.resolvesAliases = true - panel.allowedFileTypes = ["opml", "xml"] + panel.allowedContentTypes = [UTType.opml, UTType.xml] panel.allowsOtherFileTypes = false panel.beginSheetModal(for: hostWindow!) { modalResult in @@ -104,3 +105,8 @@ class ImportOPMLWindowController: NSWindowController { } } + +extension UTType { + + static let opml = UTType("public.opml")! +} diff --git a/Mac/Preferences/General/GeneralPrefencesViewController.swift b/Mac/Preferences/General/GeneralPrefencesViewController.swift index 19fa9b7a0..7fb93a71a 100644 --- a/Mac/Preferences/General/GeneralPrefencesViewController.swift +++ b/Mac/Preferences/General/GeneralPrefencesViewController.swift @@ -10,6 +10,7 @@ import AppKit import RSCore import RSWeb import UserNotifications +import UniformTypeIdentifiers final class GeneralPreferencesViewController: NSViewController { @@ -125,7 +126,7 @@ private extension GeneralPreferencesViewController { let item = NSMenuItem(title: name, action: nil, keyEquivalent: "") item.representedObject = browser.bundleIdentifier - let icon = browser.icon ?? NSWorkspace.shared.icon(forFileType: kUTTypeApplicationBundle as String) + let icon = browser.icon ?? NSWorkspace.shared.icon(for: UTType.applicationBundle) icon.size = NSSize(width: 16.0, height: 16.0) item.image = browser.icon menu.addItem(item) diff --git a/Shared/Activity/ActivityManager.swift b/Shared/Activity/ActivityManager.swift index d5e36eec8..b69c67d0c 100644 --- a/Shared/Activity/ActivityManager.swift +++ b/Shared/Activity/ActivityManager.swift @@ -13,6 +13,7 @@ import RSCore import Account import Articles import Intents +import UniformTypeIdentifiers class ActivityManager { @@ -246,7 +247,7 @@ private extension ActivityManager { func updateSelectingActivityFeedSearchAttributes(with feed: Feed) { - let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) + let attributeSet = CSSearchableItemAttributeSet(contentType: UTType.compositeContent) attributeSet.title = feed.nameForDisplay attributeSet.keywords = makeKeywords(feed.nameForDisplay) attributeSet.relatedUniqueIdentifier = ActivityManager.identifier(for: feed) @@ -265,7 +266,7 @@ private extension ActivityManager { // itself because the relatedUniqueIdentifier on the activity attributeset is populated. if let attributeSet = activity.contentAttributeSet { let identifier = attributeSet.relatedUniqueIdentifier - let tempAttributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String) + let tempAttributeSet = CSSearchableItemAttributeSet(contentType: UTType.item) let searchableItem = CSSearchableItem(uniqueIdentifier: identifier, domainIdentifier: nil, attributeSet: tempAttributeSet) CSSearchableIndex.default().indexSearchableItems([searchableItem]) } diff --git a/Shared/Favicons/FaviconDownloader.swift b/Shared/Favicons/FaviconDownloader.swift index 7a2d96053..1bc77d2ea 100644 --- a/Shared/Favicons/FaviconDownloader.swift +++ b/Shared/Favicons/FaviconDownloader.swift @@ -11,6 +11,7 @@ import CoreServices import Articles import Account import RSCore +import UniformTypeIdentifiers extension Notification.Name { @@ -61,8 +62,6 @@ final class FaviconDownloader { loadHomePageToFaviconURLCache() loadHomePageURLsWithNoFaviconURLCache() - FaviconURLFinder.ignoredTypes = [kUTTypeScalableVectorGraphics as String] - NotificationCenter.default.addObserver(self, selector: #selector(didLoadFavicon(_:)), name: .DidLoadFavicon, object: nil) } diff --git a/Shared/Favicons/FaviconURLFinder.swift b/Shared/Favicons/FaviconURLFinder.swift index dd51da4a5..28f1c20c3 100644 --- a/Shared/Favicons/FaviconURLFinder.swift +++ b/Shared/Favicons/FaviconURLFinder.swift @@ -9,32 +9,12 @@ import Foundation import CoreServices import RSParser +import UniformTypeIdentifiers // The favicon URLs may be specified in the head section of the home page. struct FaviconURLFinder { - private static var ignoredMimeTypes = [String]() - private static var ignoredExtensions = [String]() - - /// Uniform types to ignore when finding favicon URLs. - static var ignoredTypes: [String]? { - didSet { - guard let ignoredTypes = ignoredTypes else { - return - } - - for type in ignoredTypes { - if let mimeTypes = UTTypeCopyAllTagsWithClass(type as CFString, kUTTagClassMIMEType)?.takeRetainedValue() { - ignoredMimeTypes.append(contentsOf: mimeTypes as! [String]) - } - if let extensions = UTTypeCopyAllTagsWithClass(type as CFString, kUTTagClassFilenameExtension)?.takeRetainedValue() { - ignoredExtensions.append(contentsOf: extensions as! [String]) - } - } - } - } - /// Finds favicon URLs in a web page. /// - Parameters: /// - homePageURL: The page to search. @@ -49,23 +29,33 @@ struct FaviconURLFinder { // If the favicon has an explicit type, check that for an ignored type; otherwise, check the file extension. HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (htmlMetadata) in - let faviconURLs = htmlMetadata?.favicons.compactMap({ (favicon) -> String? in - if let type = favicon.type { - if ignoredMimeTypes.contains(type) { - return nil - } - } - else { - if let urlString = favicon.urlString, let url = URL(string: urlString), ignoredExtensions.contains(url.pathExtension) { - return nil - } - } - - return favicon.urlString - }) + let faviconURLs = htmlMetadata?.favicons.compactMap { + shouldAllowFavicon($0) ? $0.urlString : nil + } completion(faviconURLs) } } + + private static let ignoredTypes = [UTType.svg] + + private static func shouldAllowFavicon(_ favicon: RSHTMLMetadataFavicon) -> Bool { + + // Check mime type. + if let mimeType = favicon.type, let utType = UTType(mimeType: mimeType) { + if Self.ignoredTypes.contains(utType) { + return false + } + } + + // Check file extension. + if let urlString = favicon.urlString, let url = URL(string: urlString), let utType = UTType(filenameExtension: url.pathExtension) { + if Self.ignoredTypes.contains(utType) { + return false + } + } + + return true + } }