diff --git a/App/CompactViewController.swift b/App/CompactViewController.swift index 6af26499..d64003d5 100644 --- a/App/CompactViewController.swift +++ b/App/CompactViewController.swift @@ -37,7 +37,7 @@ final class CompactViewController: UIHostingController, UISearchControl init(navigation: NavigationViewModel) { self.navigation = navigation - searchViewModel = SearchViewModel() + searchViewModel = SearchViewModel.shared let searchResult = SearchResults().environmentObject(searchViewModel) searchController = UISearchController(searchResultsController: UIHostingController(rootView: searchResult)) super.init(rootView: AnyView(CompactView())) diff --git a/Model/Defaulting.swift b/Model/Defaulting.swift index 795ef8df..ccdd4e31 100644 --- a/Model/Defaulting.swift +++ b/Model/Defaulting.swift @@ -21,7 +21,7 @@ public protocol Defaulting: NSObjectProtocol { } final class UDefaults: NSObject, Defaulting { - subscript(key: Defaults.Key) -> Value where Value: DefaultsSerializable { + subscript(key: Defaults.Key) -> Value { get { Defaults[key] } diff --git a/SwiftUI/Model/DefaultKeys.swift b/SwiftUI/Model/DefaultKeys.swift index 2b449b71..64040a14 100644 --- a/SwiftUI/Model/DefaultKeys.swift +++ b/SwiftUI/Model/DefaultKeys.swift @@ -52,38 +52,3 @@ extension Defaults.Keys { static let windowURLs = Key<[URL]>("windowURLs", default: []) #endif } - -extension Defaults.Serializable where Self: Codable { - public static var bridge: Defaults.TopLevelCodableBridge { Defaults.TopLevelCodableBridge() } -} - -extension Defaults.Serializable where Self: Codable & NSSecureCoding { - public static var bridge: Defaults.CodableNSSecureCodingBridge { Defaults.CodableNSSecureCodingBridge() } -} - -extension Defaults.Serializable where Self: Codable & NSSecureCoding & Defaults.PreferNSSecureCoding { - public static var bridge: Defaults.NSSecureCodingBridge { Defaults.NSSecureCodingBridge() } -} - -extension Defaults.Serializable where Self: Codable & RawRepresentable { - public static var bridge: Defaults.RawRepresentableCodableBridge { Defaults.RawRepresentableCodableBridge() } -} - -extension Defaults.Serializable where Self: Codable & RawRepresentable & Defaults.PreferRawRepresentable { - public static var bridge: Defaults.RawRepresentableBridge { Defaults.RawRepresentableBridge() } -} - -extension Defaults.Serializable where Self: RawRepresentable { - public static var bridge: Defaults.RawRepresentableBridge { Defaults.RawRepresentableBridge() } -} -extension Defaults.Serializable where Self: NSSecureCoding { - public static var bridge: Defaults.NSSecureCodingBridge { Defaults.NSSecureCodingBridge() } -} - -extension Defaults.CollectionSerializable where Element: Defaults.Serializable { - public static var bridge: Defaults.CollectionBridge { Defaults.CollectionBridge() } -} - -extension Defaults.SetAlgebraSerializable where Element: Defaults.Serializable & Hashable { - public static var bridge: Defaults.SetAlgebraBridge { Defaults.SetAlgebraBridge() } -} diff --git a/Tests/TestDefaults.swift b/Tests/TestDefaults.swift index c122614d..5cc3d8d6 100644 --- a/Tests/TestDefaults.swift +++ b/Tests/TestDefaults.swift @@ -19,7 +19,7 @@ import Defaults final class TestDefaults: NSObject, Defaulting { - var dict: [Defaults.AnyKey: any DefaultsSerializable] = [:] + var dict: [Defaults._AnyKey: AnyObject] = [:] func setup() { self[.categoriesToLanguages] = [:] @@ -29,13 +29,13 @@ final class TestDefaults: NSObject, Defaulting { self[.libraryLanguageCodes] = Set() } - subscript(key: Defaults.Key) -> Value where Value: DefaultsSerializable { + subscript(key: Defaults.Key) -> Value { get { // swiftlint:disable:next force_cast dict[key] as! Value } set { - dict[key] = newValue + dict[key] = newValue as AnyObject } } } diff --git a/ViewModel/BrowserViewModel.swift b/ViewModel/BrowserViewModel.swift index a584af8b..ab7c9e0e 100644 --- a/ViewModel/BrowserViewModel.swift +++ b/ViewModel/BrowserViewModel.swift @@ -259,17 +259,33 @@ final class BrowserViewModel: NSObject, ObservableObject, @MainActor func updateLastOpened() { - guard let tab = try? Database.shared.viewContext.existingObject(with: tabID) as? Tab else { return } - tab.lastOpened = Date() + let currentTabID = tabID + Task { + Database.shared.performBackgroundTask { context in + guard let tab = try? context.existingObject(with: currentTabID) as? Tab else { + return + } + tab.lastOpened = Date() + try? context.save() + } + } } @MainActor func persistState() { - guard let tab = try? Database.shared.viewContext.existingObject(with: tabID) as? Tab else { - return + let webData = webView.interactionState as? Data + let currentTabID = tabID + Task { + Database.shared.performBackgroundTask { context in + guard let tab = try? context.existingObject(with: currentTabID) as? Tab else { + return + } + tab.interactionState = webData + if context.hasChanges { + try? context.save() + } + } } - tab.interactionState = webView.interactionState as? Data - try? Database.shared.viewContext.save() } // MARK: - Content Loading diff --git a/ViewModel/NavigationViewModel.swift b/ViewModel/NavigationViewModel.swift index 322be8d2..37b88768 100644 --- a/ViewModel/NavigationViewModel.swift +++ b/ViewModel/NavigationViewModel.swift @@ -97,6 +97,7 @@ final class NavigationViewModel: ObservableObject { /// Delete a single tab, and select another tab /// - Parameter tabID: ID of the tab to delete func deleteTab(tabID: NSManagedObjectID) { + let currentItemValue = currentItem Database.shared.performBackgroundTask { context in let sortByCreation = [NSSortDescriptor(key: "created", ascending: false)] guard let tabs: [Tab] = try? context.fetch(Tab.fetchRequest(predicate: nil, @@ -105,7 +106,7 @@ final class NavigationViewModel: ObservableObject { return } let newlySelectedTab: Tab? - if case let .tab(selectedTabID) = self.currentItem, selectedTabID == tabID { + if case let .tab(selectedTabID) = currentItemValue, selectedTabID == tabID { // select a closeBy tab if the currently selected tab is to be deleted newlySelectedTab = tabs.closeBy(toWhere: { $0.objectID == tabID }) ?? Self.makeTab(context: context) } else if tabs.count == 1 { diff --git a/ViewModel/SearchViewModel.swift b/ViewModel/SearchViewModel.swift index 28bb1971..f0b50883 100644 --- a/ViewModel/SearchViewModel.swift +++ b/ViewModel/SearchViewModel.swift @@ -23,13 +23,15 @@ final class SearchViewModel: NSObject, ObservableObject, NSFetchedResultsControl @Published private(set) var zimFiles: [UUID: ZimFile] // ID of zim files that are included in search @Published private(set) var inProgress = false @Published private(set) var results = [SearchResult]() + + static let shared = SearchViewModel() private let fetchedResultsController: NSFetchedResultsController private var searchSubscriber: AnyCancellable? @ZimActor private let queue = OperationQueue() - override init() { + override private init() { // initialize fetched results controller let predicate = NSPredicate(format: "includedInSearch == true AND fileURLBookmark != nil") fetchedResultsController = NSFetchedResultsController( @@ -39,7 +41,7 @@ final class SearchViewModel: NSObject, ObservableObject, NSFetchedResultsControl cacheName: nil ) - // initilze zim file IDs + // initialize zim file IDs try? fetchedResultsController.performFetch() zimFiles = fetchedResultsController.fetchedObjects?.reduce(into: [:]) { result, zimFile in result?[zimFile.fileID] = zimFile diff --git a/Views/Bookmarks.swift b/Views/Bookmarks.swift index 1af813be..0f9abff6 100644 --- a/Views/Bookmarks.swift +++ b/Views/Bookmarks.swift @@ -29,7 +29,7 @@ struct Bookmarks: View { var body: some View { LazyVGrid(columns: ([gridItem]), spacing: 12) { - ForEach(bookmarks) { bookmark in + ForEach(bookmarks, id: \.self) { bookmark in Button { NotificationCenter.openURL(bookmark.articleURL) if horizontalSizeClass == .compact { @@ -54,8 +54,8 @@ struct Bookmarks: View { Message(text: LocalString.bookmark_overlay_empty_title) } } +#if os(iOS) .toolbar { - #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { if #unavailable(iOS 16), horizontalSizeClass == .regular { Button { @@ -65,8 +65,8 @@ struct Bookmarks: View { } } } - #endif } +#endif } private var gridItem: GridItem { diff --git a/Views/BrowserTab.swift b/Views/BrowserTab.swift index 0ec4ef8a..a7763ff8 100644 --- a/Views/BrowserTab.swift +++ b/Views/BrowserTab.swift @@ -21,7 +21,7 @@ struct BrowserTab: View { @Environment(\.scenePhase) private var scenePhase @EnvironmentObject private var browser: BrowserViewModel @EnvironmentObject private var library: LibraryViewModel - @StateObject private var search = SearchViewModel() + @StateObject private var search = SearchViewModel.shared var body: some View { let model = if FeatureFlags.hasLibrary { diff --git a/project.yml b/project.yml index 1edfc49e..2ce20703 100644 --- a/project.yml +++ b/project.yml @@ -46,7 +46,7 @@ settings: packages: Defaults: url: https://github.com/sindresorhus/Defaults - majorVersion: 6.0.0 + majorVersion: 8.2.0 StripeApplePay: url: https://github.com/CodeLikeW/stripe-apple-pay majorVersion: 24.0.0