Web 浏览器的工作历史
How work history of web browser under the hood
我正在尝试为 iOS 上的 WKWebView 实现我自己的网络浏览器历史记录,但我无法完全实现此功能,而且每次都遇到麻烦。
我可以在用户所在的位置创建历史记录,然后在历史记录中前后移动。
但是我遇到了下一个麻烦,我认为这只是我遇到的许多问题中的一个。
例如,当我有一个包含 10 个元素的历史记录时,然后我回到第 5 个元素,然后不要前进但尝试打开新的 link 我无法删除元素 6-10 并放置新 link.
我认为我的问题是我无法完全理解历史在所有浏览器中的工作原理,这不是一项艰巨的任务,但我对这个算法感到困惑。
我保存历史的主要数据结构
帮助我了解如何在浏览器中使用此算法,或者可能存在关于它的良好理论?
我已经解决了这个问题并且很好地实现了完整的算法,完整的项目在这里可用:https://github.com/IhorYachmenov/Custom-browser-history-for-WKWebView。
算法:
struct PlayerHistory {
static var shared = PlayerHistory()
var historyExist: Bool = false
var historyCurrentPosition: Int = 0
var historyLastPositionBeforeUpdatingHistory: Int!
var userHistoryKey: String!
var backPressed: Bool!
var forwardPressed: Bool!
var urlOfPlayer: String!
// Function only for first loading inside <viewDidLoad or another method from app LifeCycle>.
mutating func getUrlForFirstLoading(initURL: String, key: String) -> String {
urlOfPlayer = initURL
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
updateFirstElement(key: key, url: initURL)
return initURL
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return initURL
}
let position = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.count - 1
historyExist = true
historyCurrentPosition = position
userHistoryKey = key
let initUrlFromHistoryStorage = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.last!.url
return initUrlFromHistoryStorage
}
// Create new or update exist history, use this method indsede <decidePolicyForNavigation>.
mutating func updatePlayerHistory(backlisk: [String], key: String) {
var history = [WebViewHistory]()
for i in backlisk {
history.append(WebViewHistory(i))
}
if (historyExist == true) {
// If old history exist need compound both and then to save.
let oldHistory = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let oldAndNewHostoryTogether = oldHistory! + history
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(oldAndNewHostoryTogether, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
} else {
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
historyExist = true
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
}
}
// Before using this method check if result don't equals nil. Use this method for navigation beetween history
func moveThroughHistory(key: String, direction: Bool) -> String? {
guard historyExist != false else {
return nil
}
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
if (direction == true) {
let index = historyCurrentPosition + 1
guard index != history.count else { return nil }
return history[index].url
} else {
let index = historyCurrentPosition - 1
guard index > 0 else { return history[0].url }
return history[index].url
}
}
// Method <setCurrentPosition> each time set position at history
mutating func setCurrentPosition(url: String, key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else { return }
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else { return }
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let index = history?.firstIndex(of: WebViewHistory(url))
guard index != nil else {
historyCurrentPosition = 0
return
}
historyCurrentPosition = index!
}
// <removeUnusedPeaceOfHistory> need use when user want open new page staying inside the middle of history
mutating func removeUnusedPeaceOfHistory(key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
return
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return
}
var history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let startIndex = historyCurrentPosition + 1
let endIndex = history.endIndex - 1
let countOfAllElements = history.count
guard startIndex != countOfAllElements else { return }
let range = startIndex...endIndex
history.removeSubrange(range)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
}
// Use <updateFirstElement> inside <getUrlForFirstLoading> if history doesn't exist
private mutating func updateFirstElement(key: String, url: String) {
var history = [WebViewHistory]()
history.insert(WebViewHistory(url), at: 0)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.saveHistory(keyValuePair)
historyExist = true
historyCurrentPosition = 0
}
// Use <webViewWillBeClosedSaveHistory> when WKWebView should be closed, if the user moves through history new position will be saved.
mutating func webViewWillBeClosedSaveHistory(key: String) {
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let currentPosition = historyCurrentPosition + 1
guard currentPosition != history.count else { return }
removeUnusedPeaceOfHistory(key: key)
}
}
我正在尝试为 iOS 上的 WKWebView 实现我自己的网络浏览器历史记录,但我无法完全实现此功能,而且每次都遇到麻烦。 我可以在用户所在的位置创建历史记录,然后在历史记录中前后移动。 但是我遇到了下一个麻烦,我认为这只是我遇到的许多问题中的一个。 例如,当我有一个包含 10 个元素的历史记录时,然后我回到第 5 个元素,然后不要前进但尝试打开新的 link 我无法删除元素 6-10 并放置新 link.
我认为我的问题是我无法完全理解历史在所有浏览器中的工作原理,这不是一项艰巨的任务,但我对这个算法感到困惑。 我保存历史的主要数据结构 帮助我了解如何在浏览器中使用此算法,或者可能存在关于它的良好理论?
我已经解决了这个问题并且很好地实现了完整的算法,完整的项目在这里可用:https://github.com/IhorYachmenov/Custom-browser-history-for-WKWebView。
算法:
struct PlayerHistory {
static var shared = PlayerHistory()
var historyExist: Bool = false
var historyCurrentPosition: Int = 0
var historyLastPositionBeforeUpdatingHistory: Int!
var userHistoryKey: String!
var backPressed: Bool!
var forwardPressed: Bool!
var urlOfPlayer: String!
// Function only for first loading inside <viewDidLoad or another method from app LifeCycle>.
mutating func getUrlForFirstLoading(initURL: String, key: String) -> String {
urlOfPlayer = initURL
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
updateFirstElement(key: key, url: initURL)
return initURL
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return initURL
}
let position = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.count - 1
historyExist = true
historyCurrentPosition = position
userHistoryKey = key
let initUrlFromHistoryStorage = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!.last!.url
return initUrlFromHistoryStorage
}
// Create new or update exist history, use this method indsede <decidePolicyForNavigation>.
mutating func updatePlayerHistory(backlisk: [String], key: String) {
var history = [WebViewHistory]()
for i in backlisk {
history.append(WebViewHistory(i))
}
if (historyExist == true) {
// If old history exist need compound both and then to save.
let oldHistory = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let oldAndNewHostoryTogether = oldHistory! + history
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(oldAndNewHostoryTogether, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
} else {
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
historyExist = true
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
setCurrentPosition(url: backlisk.last!, key: key)
}
}
// Before using this method check if result don't equals nil. Use this method for navigation beetween history
func moveThroughHistory(key: String, direction: Bool) -> String? {
guard historyExist != false else {
return nil
}
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
if (direction == true) {
let index = historyCurrentPosition + 1
guard index != history.count else { return nil }
return history[index].url
} else {
let index = historyCurrentPosition - 1
guard index > 0 else { return history[0].url }
return history[index].url
}
}
// Method <setCurrentPosition> each time set position at history
mutating func setCurrentPosition(url: String, key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else { return }
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else { return }
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]
let index = history?.firstIndex(of: WebViewHistory(url))
guard index != nil else {
historyCurrentPosition = 0
return
}
historyCurrentPosition = index!
}
// <removeUnusedPeaceOfHistory> need use when user want open new page staying inside the middle of history
mutating func removeUnusedPeaceOfHistory(key: String) {
guard HistoryStorage.shared.getHistoryFromUserDefaults() != nil else {
return
}
guard HistoryStorage.shared.getHistoryFromUserDefaults()![key] != nil else {
return
}
var history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let startIndex = historyCurrentPosition + 1
let endIndex = history.endIndex - 1
let countOfAllElements = history.count
guard startIndex != countOfAllElements else { return }
let range = startIndex...endIndex
history.removeSubrange(range)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.removeHistory()
HistoryStorage.shared.saveHistory(keyValuePair)
}
// Use <updateFirstElement> inside <getUrlForFirstLoading> if history doesn't exist
private mutating func updateFirstElement(key: String, url: String) {
var history = [WebViewHistory]()
history.insert(WebViewHistory(url), at: 0)
var keyValuePair = Dictionary<String, [WebViewHistory]>()
keyValuePair.updateValue(history, forKey: key)
HistoryStorage.shared.saveHistory(keyValuePair)
historyExist = true
historyCurrentPosition = 0
}
// Use <webViewWillBeClosedSaveHistory> when WKWebView should be closed, if the user moves through history new position will be saved.
mutating func webViewWillBeClosedSaveHistory(key: String) {
let history = HistoryStorage.shared.getHistoryFromUserDefaults()![key]!
let currentPosition = historyCurrentPosition + 1
guard currentPosition != history.count else { return }
removeUnusedPeaceOfHistory(key: key)
}
}