// // BrowserHistory.swift // App // // Created by James Magahern on 8/14/20. // import Foundation import CoreData class BrowserHistory { static public let shared = BrowserHistory() lazy fileprivate var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "History") container.loadPersistentStores { description, error in assert(error == nil) } return container }() public func didNavigate(toURL url: URL, title: String) { let dataContext = persistentContainer.viewContext let entity = HistoryItemEntity(context: dataContext) entity.url = url entity.lastVisited = Date() entity.title = title entity.host = url.host do { try dataContext.save() } catch { let nserror = error as NSError fatalError("Failed saving persistent entity to store: \(nserror), \(nserror.userInfo)") } } public func allHistory() -> [HistoryItem] { let dataContext = persistentContainer.viewContext let fetchRequest: NSFetchRequest = HistoryItemEntity.fetchRequest() let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? [] return entities.map { (entity) -> HistoryItem in HistoryItem(entity: entity) } } public func visitedToplevelHistoryItems(matching: String) -> [HistoryItem] { let dataContext = persistentContainer.viewContext let fetchRequest: NSFetchRequest = HistoryItemEntity.fetchRequest() fetchRequest.predicate = NSPredicate(format: "host CONTAINS %@ OR title contains %@", matching, matching) let entities: [HistoryItemEntity] = (try? dataContext.fetch(fetchRequest)) ?? [] let allItems: [HistoryItem] = entities.map { HistoryItem(entity: $0) } var topLevelItems: [URL: (HistoryItem, Int)] = [:] allItems.forEach { item in let topLevelURL = item.url.topLevelURL() var topLevelItem = topLevelItems[topLevelURL] ?? (item, 0) topLevelItem.0.url = topLevelURL topLevelItems[topLevelURL] = topLevelItem } return topLevelItems.values.map { return $0.0 }.sorted { (item1, item2) -> Bool in return topLevelItems[item1.url]!.1 < topLevelItems[item2.url]!.1 } } } extension URL { public func topLevelURL() -> URL { if var components = URLComponents(url: self, resolvingAgainstBaseURL: false) { components.path = "" components.query = "" components.queryItems = [] if let url = components.url { return url } } return self } }