Skip to content

Commit

Permalink
Merge pull request #989 from kiwix/remove-ordered-dependency
Browse files Browse the repository at this point in the history
Remove OrderedCollections dependency
  • Loading branch information
kelson42 authored Sep 27, 2024
2 parents 784bb96 + 939806f commit b0db47c
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 15 deletions.
63 changes: 63 additions & 0 deletions Tests/OrderedCacheTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// This file is part of Kiwix for iOS & macOS.
//
// Kiwix is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// any later version.
//
// Kiwix is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kiwix; If not, see https://www.gnu.org/licenses/.

import XCTest
@testable import Kiwix

final class OrderedCacheTests: XCTestCase {

@MainActor
func testEmpty() {
let cache = OrderedCache<String, String>()
XCTAssertEqual(cache.count, 0)
XCTAssertNil(cache.findBy(key: "not to be found"))
}

@MainActor
func testOneItem() {
let cache = OrderedCache<String, String>()
let pastDate = Date.distantPast
cache.setValue("test_value", forKey: "keyOne", dated: pastDate)
XCTAssertEqual(cache.count, 1)
XCTAssertNil(cache.findBy(key: "not to be found"))
XCTAssertEqual(cache.findBy(key: "keyOne"), "test_value")
cache.removeOlderThan(pastDate)
XCTAssertEqual(cache.count, 1)
cache.removeOlderThan(Date.now)
XCTAssertEqual(cache.count, 0)
}

@MainActor
func testRemoveOlderThan() {
let cache = OrderedCache<String, String>()
let nowDate = Date.now
cache.setValue("test_value", forKey: "keyOne", dated: nowDate)
cache.setValue("old_value", forKey: "keyOld", dated: Date.distantPast)
XCTAssertEqual(cache.count, 2)
cache.removeOlderThan(nowDate.advanced(by: -1))
XCTAssertEqual(cache.count, 1)
}

@MainActor
func testRemoveByKey() {
let cache = OrderedCache<String, Int>()
cache.setValue(1, forKey: "one")
cache.setValue(0, forKey: "zero")
cache.removeValue(forKey: "zero")
XCTAssertNil(cache.findBy(key: "zero"))
XCTAssertEqual(cache.findBy(key: "one"), 1)
}

}
24 changes: 13 additions & 11 deletions ViewModel/BrowserViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,34 @@ import CoreLocation
import WebKit
import Defaults
import os

import OrderedCollections
import CoreKiwix

// swiftlint:disable file_length
// swiftlint:disable:next type_body_length
final class BrowserViewModel: NSObject, ObservableObject,
WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate,
NSFetchedResultsControllerDelegate {
private static var cache = OrderedDictionary<NSManagedObjectID, BrowserViewModel>()
@MainActor
private static var cache: OrderedCache<NSManagedObjectID, BrowserViewModel>?

@MainActor
static func getCached(tabID: NSManagedObjectID) -> BrowserViewModel {
let viewModel = cache[tabID] ?? BrowserViewModel(tabID: tabID)
cache.removeValue(forKey: tabID)
cache[tabID] = viewModel
if let cachedModel = cache?.findBy(key: tabID) {
return cachedModel
}
if cache == nil {
cache = .init()
}
let viewModel = BrowserViewModel(tabID: tabID)
cache?.removeValue(forKey: tabID)
cache?.setValue(viewModel, forKey: tabID)
return viewModel
}

static func purgeCache() {
guard cache.count > 10 else { return }
let range = 0 ..< cache.count - 5
cache.values[range].forEach { viewModel in
viewModel.persistState()
Task { @MainActor in
cache?.removeOlderThan(Date.now.advanced(by: -360)) // 6 minutes
}
cache.removeSubrange(range)
}

// MARK: - Properties
Expand Down
56 changes: 56 additions & 0 deletions ViewModel/OrderedCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This file is part of Kiwix for iOS & macOS.
//
// Kiwix is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// any later version.
//
// Kiwix is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Kiwix; If not, see https://www.gnu.org/licenses/.

import Foundation

@MainActor
final class OrderedCache<Key: Hashable, Value> {

private struct ValueDated<V> {
let value: V
let date: Date
}

private var dict: [Key: ValueDated<Value>] = [:]

var count: Int {
dict.count
}

func findBy(key: Key) -> Value? {
if let dateValue = dict[key] {
return dateValue.value
}
return nil
}

func removeAll() {
dict = [:]
}

func removeOlderThan(_ pastDate: Date) {
dict = dict.filter { (_, value: ValueDated<Value>) in
value.date >= pastDate
}
}

func setValue(_ value: Value, forKey key: Key, dated: Date = Date.now) {
dict[key] = ValueDated(value: value, date: dated)
}

func removeValue(forKey key: Key) {
dict.removeValue(forKey: key)
}
}
4 changes: 0 additions & 4 deletions project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ packages:
Defaults:
url: https://github.com/sindresorhus/Defaults
majorVersion: 6.0.0
OrderedCollections:
url: https://github.com/apple/swift-collections.git
majorVersion: 1.0.4

targetTemplates:
ApplicationTemplate:
Expand All @@ -73,7 +70,6 @@ targetTemplates:
- sdk: NotificationCenter.framework
- sdk: QuickLook.framework
- package: Defaults
- package: OrderedCollections
sources:
- path: App
- path: Model
Expand Down

0 comments on commit b0db47c

Please sign in to comment.