Started working on history tracking

This commit is contained in:
James Magahern
2020-08-14 20:05:36 -07:00
parent c6adf599f4
commit c6a27b3fd9
5 changed files with 171 additions and 1 deletions

View File

@@ -0,0 +1,88 @@
//
// 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> = 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> = 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
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17189" systemVersion="20A2348b" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="HistoryItemEntity" representedClassName="HistoryItemEntity" syncable="YES" codeGenerationType="class">
<attribute name="host" optional="YES" attributeType="String"/>
<attribute name="lastVisited" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="title" optional="YES" attributeType="String"/>
<attribute name="url" optional="YES" attributeType="URI"/>
<attribute name="visitCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
</entity>
<elements>
<element name="HistoryItemEntity" positionX="-63" positionY="-18" width="128" height="104"/>
</elements>
</model>

View File

@@ -0,0 +1,21 @@
//
// HistoryItem.swift
// App
//
// Created by James Magahern on 8/14/20.
//
import Foundation
struct HistoryItem: Hashable
{
var url: URL
var title: String
var lastVisited: Date
init(entity: HistoryItemEntity) {
self.url = entity.url ?? URL(string: "about:blank")!
self.lastVisited = entity.lastVisited ?? Date()
self.title = entity.title ?? ""
}
}

View File

@@ -273,14 +273,20 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
updateTitleAndURL(forWebView: webView)
// Start requesting favicon
if let url = webView.url {
// Start requesting favicon
tab.updateFaviconForURL(url)
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
toolbarController.urlBar.loadProgress = .complete
// Update history
if let url = webView.url {
let title = webView.title ?? ""
BrowserHistory.shared.didNavigate(toURL: url, title: title)
}
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
@@ -305,6 +311,15 @@ class BrowserViewController: UIViewController, WKNavigationDelegate,
// MARK: UITextField Delegate
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text {
let matches = BrowserHistory.shared.visitedToplevelHistoryItems(matching: text)
print(matches)
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let text = textField.text?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) {