swiftie has moved to separated lib
This commit is contained in:
parent
f3508e0d1b
commit
fe24e0f442
|
@ -34,7 +34,7 @@
|
|||
7898F68127D4023C00716057 /* CitySearchModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7898F67A27D4023C00716057 /* CitySearchModels.swift */; };
|
||||
7898F68227D4023C00716057 /* CitySearchWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7898F67B27D4023C00716057 /* CitySearchWorker.swift */; };
|
||||
7898F68327D4023C00716057 /* CitySearchRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7898F67C27D4023C00716057 /* CitySearchRouter.swift */; };
|
||||
78C15DC927D4B139002FA4E5 /* Swiftrie in Frameworks */ = {isa = PBXBuildFile; productRef = 78C15DC827D4B139002FA4E5 /* Swiftrie */; };
|
||||
78C14BBD28E4EF0000A1FA7C /* Swiftrie in Frameworks */ = {isa = PBXBuildFile; productRef = 78C14BBC28E4EF0000A1FA7C /* Swiftrie */; };
|
||||
78C15DCF27D508D9002FA4E5 /* CityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C15DCD27D508D9002FA4E5 /* CityCell.swift */; };
|
||||
78C15DD027D508D9002FA4E5 /* CityCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78C15DCE27D508D9002FA4E5 /* CityCell.xib */; };
|
||||
78C15DD927D5348F002FA4E5 /* CityMap.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 78C15DD227D5348F002FA4E5 /* CityMap.storyboard */; };
|
||||
|
@ -113,7 +113,6 @@
|
|||
78F19CA827D68F4E003BE8B2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
78F19CAA27D68F60003BE8B2 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
78F19CAC27D69006003BE8B2 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
|
||||
78FC699D27D394A80088343D /* Swiftrie */ = {isa = PBXFileReference; lastKnownFileType = text; path = Swiftrie; sourceTree = SOURCE_ROOT; };
|
||||
78FC69A627D3B15D0088343D /* API */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = API; sourceTree = SOURCE_ROOT; };
|
||||
78FC69AA27D3B6D10088343D /* cities.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = cities.json; sourceTree = SOURCE_ROOT; };
|
||||
78FC69AC27D3BA590088343D /* Extensions */ = {isa = PBXFileReference; lastKnownFileType = text; path = Extensions; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -126,7 +125,7 @@
|
|||
files = (
|
||||
7898F67327D3BB3D00716057 /* Extensions in Frameworks */,
|
||||
78FC69A827D3B51C0088343D /* API in Frameworks */,
|
||||
78C15DC927D4B139002FA4E5 /* Swiftrie in Frameworks */,
|
||||
78C14BBD28E4EF0000A1FA7C /* Swiftrie in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -183,7 +182,6 @@
|
|||
780ABAF727D37B1800AC241A /* Info.plist */,
|
||||
78FC69AC27D3BA590088343D /* Extensions */,
|
||||
78FC69A627D3B15D0088343D /* API */,
|
||||
78FC699D27D394A80088343D /* Swiftrie */,
|
||||
78F19CA927D68F4E003BE8B2 /* Localizable.strings */,
|
||||
);
|
||||
path = CitieSearch;
|
||||
|
@ -349,7 +347,7 @@
|
|||
packageProductDependencies = (
|
||||
78FC69A727D3B51C0088343D /* API */,
|
||||
7898F67227D3BB3D00716057 /* Extensions */,
|
||||
78C15DC827D4B139002FA4E5 /* Swiftrie */,
|
||||
78C14BBC28E4EF0000A1FA7C /* Swiftrie */,
|
||||
);
|
||||
productName = CitieSearch;
|
||||
productReference = 780ABAE627D37B1700AC241A /* CitieSearch.app */;
|
||||
|
@ -425,6 +423,7 @@
|
|||
);
|
||||
mainGroup = 780ABADD27D37B1700AC241A;
|
||||
packageReferences = (
|
||||
78C14BBB28E4EF0000A1FA7C /* XCRemoteSwiftPackageReference "Swiftrie" */,
|
||||
);
|
||||
productRefGroup = 780ABAE727D37B1700AC241A /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -1009,13 +1008,25 @@
|
|||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
78C14BBB28E4EF0000A1FA7C /* XCRemoteSwiftPackageReference "Swiftrie" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/yucelokan/Swiftrie.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
7898F67227D3BB3D00716057 /* Extensions */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Extensions;
|
||||
};
|
||||
78C15DC827D4B139002FA4E5 /* Swiftrie */ = {
|
||||
78C14BBC28E4EF0000A1FA7C /* Swiftrie */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 78C14BBB28E4EF0000A1FA7C /* XCRemoteSwiftPackageReference "Swiftrie" */;
|
||||
productName = Swiftrie;
|
||||
};
|
||||
78FC69A727D3B51C0088343D /* API */ = {
|
||||
|
|
|
@ -5,17 +5,14 @@ First in first. You need to build all packages before building the project.
|
|||
Packages:
|
||||
- API
|
||||
- Extensions
|
||||
- Swiftrie
|
||||
|
||||
* Open API folder under project files and open Package.swift (You need to build it for iPhone)
|
||||
|
||||
* Open Extensions folder under project files and open Package.swift (You need to build it for iPhone)
|
||||
|
||||
* Open Swiftrie folder under project files and open Package.swift (You need to build it for iPhone)
|
||||
|
||||
* After you build them with successfully. Please open .xcodeproj of main project and build/run it.
|
||||
|
||||
## Swiftrie
|
||||
|
||||
Please, check [my solution](https://github.com/yucelokan/CitieSearch/blob/main/Swiftrie/README.md).
|
||||
Please, check [my solution](https://github.com/yucelokan/Swiftrie).
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
|
@ -1,77 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Swiftrie"
|
||||
BuildableName = "Swiftrie"
|
||||
BlueprintName = "Swiftrie"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "SwiftrieTests"
|
||||
BuildableName = "SwiftrieTests"
|
||||
BlueprintName = "SwiftrieTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "Swiftrie"
|
||||
BuildableName = "Swiftrie"
|
||||
BlueprintName = "Swiftrie"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -1,27 +0,0 @@
|
|||
// swift-tools-version:5.5
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Swiftrie",
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "Swiftrie",
|
||||
targets: ["Swiftrie"])
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "Swiftrie",
|
||||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "SwiftrieTests",
|
||||
dependencies: ["Swiftrie"])
|
||||
]
|
||||
)
|
|
@ -1,47 +0,0 @@
|
|||
|
||||
# Swiftrie
|
||||
|
||||
First thing first, what is a [Trie](https://en.wikipedia.org/wiki/Trie).?
|
||||
|
||||
> In computer science, a trie, also called digital tree or prefix tree, is a kind of search tree —an ordered tree data structure used to store a dynamic set or associative array where the keys are usually strings. [...] All the descendants of a node have a common prefix of the string associated with that node, and the root is associated with the empty string. Keys tend to be associated with leaves, though some inner nodes may correspond to keys of interest. Hence, keys are not necessarily associated with every node.
|
||||
|
||||
This library provides all requirements about Trie implementation on Swift.
|
||||
|
||||
What does this library offer:
|
||||
|
||||
* First of all everything; `Swiftrie works with Codables`, not only with words. Every final node stores a JSON string for itself. It means you can use it with Codables.
|
||||
|
||||
* Suit for all data models. Just implement `Swiftriable Interface` and that's all you need to make your model suit for Swiftrie.
|
||||
|
||||
* A new item can be `insertable`/`removable`.
|
||||
|
||||
* `Cancelable searches`. Swiftrie does it automatically. If you call the searching method again while the algorithm is searching a prefix, Swiftrie cancels the last search.
|
||||
|
||||
* Also you can add `throttle`/`delay` while searching in Swiftrie to prevent unnecessary searches. `Default is 0`. Because `Swiftrie can show results in 0.001 seconds in 209k data for an entered character`. Because Swiftrie does not wait for all nodes that will be visited. Check the following topic.
|
||||
|
||||
* Swiftrie provides showing results part by part. No need waiting for until the algorithm visits all nodes. It returns the results while visiting the nodes. To manage this logic just use `.gradually`. Default is `case indexable(_ index: 3)` index 3 means, the find method will return the response that it found at every 3 nodes without waiting for all nodes that will be visited.
|
||||
|
||||
* Everything in a `custom queue`. QoS is `.userInitiated`. And it is `concurrent`.
|
||||
|
||||
* Inserting and removing an item executes with `.barrier` in the custom queue. It means searching/getting will wait for inserting/removing and the algorithm will show correct results always.
|
||||
|
||||
* Unit tests have provided.
|
||||
|
||||
## Codes
|
||||
```swift
|
||||
let trie = Swiftrie(swiftriables: response.cities)
|
||||
|
||||
trie.gradually = .indexable(3)
|
||||
|
||||
trie.removeItem(/* a swiftriable item */)
|
||||
|
||||
trie.insertItem(/* a swiftriable item */)
|
||||
|
||||
let cities: [City] = trie.getAllItems()
|
||||
|
||||
trie.findItems(prefix: "istanbu", throttle: 0, type: City.self) { result in
|
||||
print(result)
|
||||
}
|
||||
|
||||
trie.cancelSearch()
|
||||
```
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// Codable+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 5.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Encodable {
|
||||
var data: Data? {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .prettyPrinted
|
||||
return try? encoder.encode(self)
|
||||
}
|
||||
|
||||
var jsonString: String {
|
||||
guard let data = data else { return "" }
|
||||
guard let jsonString = String(data: data, encoding: .utf8) else { return "" }
|
||||
return jsonString
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
//
|
||||
// DispatchQueue+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension DispatchQueue {
|
||||
/**
|
||||
- parameters:
|
||||
- target: Object used as the sentinel for de-duplication.
|
||||
- delay: The time window for de-duplication to occur
|
||||
- work: The work item to be invoked on the queue.
|
||||
Performs work only once for the given target, given the time window. The last added work closure
|
||||
is the work that will finally execute.
|
||||
Note: This is currently only safe to call from the main thread.
|
||||
Example usage:
|
||||
```
|
||||
DispatchQueue.main.asyncDeduped(target: self, after: 1.0) { [weak self] in
|
||||
self?.doTheWork()
|
||||
}
|
||||
```
|
||||
*/
|
||||
func asyncDeduped(
|
||||
target: AnyObject,
|
||||
after delay: TimeInterval,
|
||||
execute work: @escaping @convention(block) () -> Void
|
||||
) {
|
||||
let dedupeIdentifier = DispatchQueue.dedupeIdentifierFor(target)
|
||||
if let existingWorkItem = DispatchQueue.workItems.removeValue(forKey: dedupeIdentifier) {
|
||||
existingWorkItem.cancel()
|
||||
}
|
||||
let workItem = DispatchWorkItem {
|
||||
DispatchQueue.workItems.removeValue(forKey: dedupeIdentifier)
|
||||
|
||||
for ptr in DispatchQueue.weakTargets.allObjects {
|
||||
if dedupeIdentifier == DispatchQueue.dedupeIdentifierFor(ptr as AnyObject) {
|
||||
work()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
DispatchQueue.workItems[dedupeIdentifier] = workItem
|
||||
DispatchQueue.weakTargets.addPointer(Unmanaged.passUnretained(target).toOpaque())
|
||||
|
||||
asyncAfter(deadline: .now() + delay, execute: workItem)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Static Properties for De-Duping
|
||||
|
||||
private extension DispatchQueue {
|
||||
static var workItems = [AnyHashable: DispatchWorkItem]()
|
||||
static var weakTargets = NSPointerArray.weakObjects()
|
||||
static func dedupeIdentifierFor(_ object: AnyObject) -> String {
|
||||
"\(Unmanaged.passUnretained(object).toOpaque())." + String(describing: object)
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
//
|
||||
// String+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 5.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
func toObject<T: Decodable>() -> T? {
|
||||
return try? JSONDecoder().decode(T.self, from: Data(self.utf8))
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// YOOrderedDictionary.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 12.04.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class YOOrderedDictionary<Key: Hashable, Value> {
|
||||
|
||||
typealias Element = (key: Key, value: Value)
|
||||
|
||||
init() { }
|
||||
|
||||
private (set) var values: [Element] = []
|
||||
|
||||
subscript(key: Key) -> Value? {
|
||||
get {
|
||||
guard let item = values.first(where: {$0.key == key}) else { return nil }
|
||||
return item.value
|
||||
}
|
||||
set(newValue) {
|
||||
guard let value = newValue else {
|
||||
// if it is nil, remove it
|
||||
values.removeAll(where: {$0.key == key})
|
||||
return
|
||||
}
|
||||
let element = (key: key, value: value)
|
||||
guard let index = values.firstIndex(where: {$0.key == key}) else {
|
||||
// if there is no exist, append it
|
||||
values.append(element)
|
||||
return
|
||||
}
|
||||
// if it already exists, remove it and insert it
|
||||
values.remove(at: index)
|
||||
values.insert(element, at: index)
|
||||
}
|
||||
}
|
||||
|
||||
func sort(
|
||||
by condition: (Element, Element) -> Bool
|
||||
) {
|
||||
values.sort(by: condition)
|
||||
}
|
||||
|
||||
var first: Value? {
|
||||
return values.first?.value
|
||||
}
|
||||
|
||||
var last: Value? {
|
||||
return values.last?.value
|
||||
}
|
||||
|
||||
var count: Int {
|
||||
return values.count
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
return values.isEmpty
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// SwiftrieItem.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 5.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Swiftriable: Codable {
|
||||
var prefixableText: String { get }
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 7.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SwiftriableFindPrimes<T: Swiftriable>: Operation {
|
||||
typealias PrimesTrie = SwiftriableLogic & SwiftrieFindableLogic & SwiftrieStorableLogic
|
||||
init(swiftriable: PrimesTrie,
|
||||
prefix: String,
|
||||
type: T.Type,
|
||||
completion: @escaping ([T]) -> Void) {
|
||||
self.swiftriable = swiftriable
|
||||
self.prefix = prefix
|
||||
self.type = type
|
||||
self.completion = completion
|
||||
}
|
||||
|
||||
private unowned var swiftriable: PrimesTrie
|
||||
private var prefix: String
|
||||
private var type: T.Type
|
||||
private var completion: ([T]) -> Void
|
||||
|
||||
override func main() {
|
||||
findResults(with: prefix, type: type, completion: completion)
|
||||
}
|
||||
|
||||
private func findResults<T: Swiftriable>(
|
||||
with prefix: String, type: T.Type? = nil, completion: (([T]) -> Void)? = nil
|
||||
) {
|
||||
let graduallyIndex = (
|
||||
swiftriable as? SwiftrieAccessibleLogic
|
||||
)?.gradually.modIndex ?? -1
|
||||
var items: [T] = []
|
||||
let prefixLowerCased = prefix.lowercased()
|
||||
if let lastNode = swiftriable.findLastNodeOf(word: prefixLowerCased) {
|
||||
if lastNode.isFinal {
|
||||
let subItems: [T] = lastNode.items.compactMap({$0.toObject()})
|
||||
items.append(contentsOf: subItems)
|
||||
}
|
||||
for item in lastNode.childrens.values.enumerated() {
|
||||
if isCancelled {
|
||||
return
|
||||
}
|
||||
let childItems: [T] = swiftriable.itemsInSubtrie(rootNode: item.element.value, partialWord: prefixLowerCased)
|
||||
items += childItems
|
||||
if graduallyIndex > 1, item.offset%graduallyIndex == 0 {
|
||||
swiftriable.queue.async {
|
||||
completion?(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completion?(items)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// SwiftriableGraduallyLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 8.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SwiftriableGraduallyLogic {
|
||||
case nonIndexable
|
||||
case indexable(_ index: Int)
|
||||
|
||||
var modIndex: Int? {
|
||||
switch self {
|
||||
case .nonIndexable:
|
||||
return nil
|
||||
case .indexable(let index):
|
||||
guard index > 1 else { return 2 }
|
||||
return index
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//
|
||||
// Swiftrie+SwiftriableLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwiftriableLogic: AnyObject {
|
||||
init(swiftriables: [Swiftriable])
|
||||
func items<T: Swiftriable>() -> [T]
|
||||
func find<T: Swiftriable>(
|
||||
with prefix: String, type: T.Type, completion: @escaping (([T]) -> Void)
|
||||
)
|
||||
}
|
||||
|
||||
extension SwiftriableLogic where Self: SwiftrieFindableLogic & SwiftrieStorableLogic {
|
||||
/// All Swiftriables currently in the trie
|
||||
func items<T: Swiftriable>() -> [T] {
|
||||
return itemsInSubtrie(rootNode: root, partialWord: "")
|
||||
}
|
||||
|
||||
/// Fetchs part by part an array of Swiftriable in a subtrie of the trie that start with the given prefix
|
||||
/// - Parameters:
|
||||
/// - prefix: the letters for word prefix
|
||||
/// - type: type of Swiftriable
|
||||
/// - completion: Swiftriables in the subtrie that start with prefix (party by part)
|
||||
func find<T: Swiftriable>(
|
||||
with prefix: String, type: T.Type, completion: @escaping (([T]) -> Void)
|
||||
) {
|
||||
operations.cancelAllOperations()
|
||||
let operation = SwiftriableFindPrimes<T>(
|
||||
swiftriable: self, prefix: prefix, type: type, completion: completion
|
||||
)
|
||||
operations.addOperation(operation)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// SwiftrieNode+SwiftriableModifiableNodeLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwifriableModifiableNodeLogic: AnyObject {
|
||||
init(value: Character?, parent: SwiftriableNode?, item: String?)
|
||||
func add(value: Character, item: String?)
|
||||
}
|
||||
|
||||
extension SwifriableModifiableNodeLogic where Self: SwiftriableStorableNodeLogic {
|
||||
/// Adds a child node to self.
|
||||
/// - Parameter value: The item to be added to this node.
|
||||
func add(value: Character, item: String? = nil) {
|
||||
childrens[value] = SwiftrieNode(value: value, parent: self, item: item)
|
||||
childrens.sort(by: {$0.key.lowercased() < $1.key.lowercased()})
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// SwiftrieNode+SwiftriableStorableNodeLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwiftriableStorableNodeLogic: AnyObject {
|
||||
var value: Character? { get set }
|
||||
var items: [String] { get set }
|
||||
var parent: SwiftriableNode? { get set }
|
||||
var childrens: YOOrderedDictionary<Character, SwiftriableNode> { get set }
|
||||
var isFinal: Bool { get set }
|
||||
var isLeaf: Bool { get }
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// Swiftrie.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias SwiftrieAllLogics = (
|
||||
SwiftriableLogic & SwiftrieStorableLogic & SwiftrieFindableLogic & SwiftrieModifiableLogic
|
||||
)
|
||||
|
||||
public class Swiftrie: SwiftrieAllLogics {
|
||||
required public init(swiftriables: [Swiftriable]) {
|
||||
swiftriables.forEach { swiftriable in
|
||||
insert(swiftriable)
|
||||
}
|
||||
}
|
||||
|
||||
var root: SwiftriableNode = SwiftrieNode(value: nil, parent: nil, item: nil)
|
||||
var operations: OperationQueue = OperationQueue()
|
||||
var queue: DispatchQueue = DispatchQueue(label: "swiftrie-safe", qos: .userInitiated, attributes: .concurrent)
|
||||
|
||||
public var gradually: SwiftriableGraduallyLogic = .indexable(3)
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// SwiftrieAccessibleLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 8.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol SwiftrieAccessibleLogic {
|
||||
|
||||
/// cancel all search thats are active
|
||||
func cancelSearch()
|
||||
|
||||
/// get all items in Swiftrie.
|
||||
/// - Returns: A Swiftriable array that is generic. Response type should be provided.
|
||||
func getAllItems<T: Swiftriable>() -> [T]
|
||||
|
||||
/// Fetchs part by part an array of Swiftriable in a subtrie of the trie that start with the given prefix
|
||||
/// - Parameters:
|
||||
/// - prefix: the letters for word prefix
|
||||
/// - throttle: delay for search / deduped
|
||||
/// - type: type of Swiftriable
|
||||
/// - completion: Swiftriables in the subtrie
|
||||
/// that start with prefix (party by part) (to change logic check .gradually)
|
||||
func findItems<T: Swiftriable>(
|
||||
prefix: String, throttle: Double, type: T.Type, completion: @escaping (([T]) -> Void)
|
||||
)
|
||||
|
||||
/// remove a Swiftriable item from Swiftrie .
|
||||
/// - Parameter Swiftriable: the Swiftriable to be removed
|
||||
func removeItem(_ swiftriable: Swiftriable)
|
||||
|
||||
/// Inserts a Swiftriable into the trie.
|
||||
/// - Parameter Swiftriable: the Swiftriable to be inserted.
|
||||
func insertItem(_ swiftriable: Swiftriable)
|
||||
|
||||
/// a logic to get data part by part. Default is `case indexable(_ index: 3)`
|
||||
/// index 3 means, the find method will return the response
|
||||
/// that it found at every 3 nodes without waiting for all nodes that will be visited.
|
||||
var gradually: SwiftriableGraduallyLogic { get set }
|
||||
}
|
||||
|
||||
extension Swiftrie: SwiftrieAccessibleLogic {
|
||||
public func cancelSearch() {
|
||||
operations.cancelAllOperations()
|
||||
}
|
||||
|
||||
public func getAllItems<T: Swiftriable>() -> [T] {
|
||||
queue.sync {
|
||||
return items()
|
||||
}
|
||||
}
|
||||
|
||||
public func findItems<T: Swiftriable>(
|
||||
prefix: String, throttle: Double, type: T.Type, completion: @escaping (([T]) -> Void)
|
||||
) {
|
||||
queue.asyncDeduped(target: self, after: throttle) { [weak self] in
|
||||
self?.queue.sync {
|
||||
self?.find(with: prefix, type: type, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func removeItem(_ swiftriable: Swiftriable) {
|
||||
queue.async(flags: .barrier) {
|
||||
self.remove(swiftriable)
|
||||
}
|
||||
}
|
||||
|
||||
public func insertItem(_ swiftriable: Swiftriable) {
|
||||
queue.async(flags: .barrier) {
|
||||
self.insert(swiftriable)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
//
|
||||
// Swiftrie+Extensions.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 5.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwiftrieFindableLogic: AnyObject {
|
||||
func findLastNodeOf(word: String) -> SwiftriableNode?
|
||||
func findTerminalNodeOf(word: String) -> SwiftriableNode?
|
||||
func deleteNodesForWordEndingWith(terminalNode: SwiftriableNode)
|
||||
func itemsInSubtrie<T: Swiftriable>(rootNode: SwiftriableNode, partialWord: String) -> [T]
|
||||
}
|
||||
|
||||
extension SwiftrieFindableLogic where Self: SwiftrieStorableLogic {
|
||||
/// Attempts to walk to the last node of a word. The
|
||||
/// search will fail if the word is not present. Doesn't
|
||||
/// check if the node is terminating
|
||||
///
|
||||
/// - Parameter word: the word in question
|
||||
/// - Returns: the node where the search ended, nil if the
|
||||
/// search failed.
|
||||
func findLastNodeOf(word: String) -> SwiftriableNode? {
|
||||
var currentNode = root
|
||||
for character in word.lowercased() {
|
||||
guard let childNode = currentNode.childrens[character] else {
|
||||
return nil
|
||||
}
|
||||
currentNode = childNode
|
||||
}
|
||||
return currentNode
|
||||
}
|
||||
|
||||
/// Attempts to walk to the terminating node of a word. The
|
||||
/// search will fail if the word is not present.
|
||||
///
|
||||
/// - Parameter word: the word in question
|
||||
/// - Returns: the node where the search ended, nil if the
|
||||
/// search failed.
|
||||
func findTerminalNodeOf(word: String) -> SwiftriableNode? {
|
||||
if let lastNode = findLastNodeOf(word: word) {
|
||||
return lastNode.isFinal ? lastNode : nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Deletes a word from the trie by starting with the last letter
|
||||
/// and moving back, deleting nodes until either a non-leaf or a
|
||||
/// terminating node is found.
|
||||
///
|
||||
/// - Parameter terminalNode: the node representing the last node
|
||||
/// of a word
|
||||
func deleteNodesForWordEndingWith(terminalNode: SwiftriableNode) {
|
||||
var lastNode = terminalNode
|
||||
var character = lastNode.value
|
||||
while lastNode.isLeaf, let parentNode = lastNode.parent {
|
||||
lastNode = parentNode
|
||||
lastNode.childrens[character ?? Character("empty")] = nil
|
||||
character = lastNode.value
|
||||
if lastNode.isFinal {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array of words in a subtrie of the trie
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - rootNode: the root node of the subtrie
|
||||
/// - partialWord: the letters collected by traversing to this node
|
||||
/// - Returns: the objects in the subtrie
|
||||
func itemsInSubtrie<T: Swiftriable>(rootNode: SwiftriableNode, partialWord: String) -> [T] {
|
||||
var subtrieItems: [T] = []
|
||||
var previousLetters = partialWord
|
||||
if let value = rootNode.value {
|
||||
previousLetters.append(value)
|
||||
}
|
||||
if rootNode.isFinal {
|
||||
let items: [T] = rootNode.items.compactMap({$0.toObject()})
|
||||
subtrieItems.append(contentsOf: items)
|
||||
}
|
||||
for item in rootNode.childrens.values {
|
||||
let childItems: [T] = itemsInSubtrie(rootNode: item.value, partialWord: previousLetters)
|
||||
subtrieItems += childItems
|
||||
}
|
||||
return subtrieItems
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
//
|
||||
// Swiftrie+SwiftrieModifiableLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 6.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwiftrieModifiableLogic {
|
||||
func remove(_ swiftriable: Swiftriable)
|
||||
func insert(_ swiftriable: Swiftriable)
|
||||
}
|
||||
|
||||
extension SwiftrieModifiableLogic where Self: SwiftrieFindableLogic & SwiftrieStorableLogic {
|
||||
/// Inserts a Swiftriable into the trie.
|
||||
/// - Parameter Swiftriable: the Swiftriable to be inserted.
|
||||
func insert(_ swiftriable: Swiftriable) {
|
||||
guard !swiftriable.prefixableText.isEmpty else { return }
|
||||
|
||||
var currentNode = root
|
||||
swiftriable.prefixableText.lowercased().forEach { character in
|
||||
if let childNode = currentNode.childrens[character] {
|
||||
currentNode = childNode
|
||||
} else {
|
||||
currentNode.add(value: character)
|
||||
if let childNode = currentNode.childrens[character] {
|
||||
currentNode = childNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Word already present?
|
||||
guard !currentNode.isFinal else {
|
||||
jsonAppender(node: currentNode, item: swiftriable)
|
||||
return
|
||||
}
|
||||
currentNode.isFinal = true
|
||||
jsonAppender(node: currentNode, item: swiftriable)
|
||||
|
||||
}
|
||||
|
||||
/// Removes a Swiftriable from the trie. If the Swiftriable is not present or
|
||||
/// it is empty, just ignore it. If the last node is a leaf,
|
||||
/// and this node has a only an item as JSON string
|
||||
/// delete that node and higher nodes that are leaves until a
|
||||
/// terminating node or non-leaf is found.
|
||||
/// If it has more then one item only remove JSON string
|
||||
/// - Parameter Swiftriable: the Swiftriable to be removed.
|
||||
func remove(_ swiftriable: Swiftriable) {
|
||||
guard !swiftriable.prefixableText.isEmpty,
|
||||
let terminalNode = findTerminalNodeOf(word: swiftriable.prefixableText) else { return }
|
||||
|
||||
guard terminalNode.isLeaf else {
|
||||
terminalNode.isFinal = false
|
||||
return
|
||||
}
|
||||
if terminalNode.items.count > 1 {
|
||||
if let index = terminalNode.items.firstIndex(where: {$0 == swiftriable.jsonString}) {
|
||||
terminalNode.items.remove(at: index)
|
||||
}
|
||||
} else {
|
||||
deleteNodesForWordEndingWith(terminalNode: terminalNode)
|
||||
}
|
||||
}
|
||||
|
||||
/// add the json if it doesn't exist
|
||||
private func jsonAppender(node: SwiftriableNode, item: Swiftriable) {
|
||||
let jsonString = item.jsonString
|
||||
if !node.items.contains(jsonString) {
|
||||
node.items.append(jsonString)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//
|
||||
// SwiftrieNode.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 5.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias SwiftriableNode = SwifriableModifiableNodeLogic & SwiftriableStorableNodeLogic
|
||||
|
||||
/// A node in the trie
|
||||
class SwiftrieNode: SwiftriableNode {
|
||||
|
||||
/// Initializes a node.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value that goes into the node
|
||||
/// - parent: A reference to this node's parent
|
||||
/// - item: a json string for the swiftriable item
|
||||
required init(value: Character?, parent: SwiftriableNode?, item: String?) {
|
||||
self.value = value
|
||||
self.parent = parent
|
||||
self.items.append(item ?? "")
|
||||
}
|
||||
|
||||
var value: Character?
|
||||
var items: [String] = []
|
||||
weak var parent: SwiftriableNode?
|
||||
var childrens: YOOrderedDictionary<Character, SwiftriableNode> = .init()
|
||||
var isFinal: Bool = false
|
||||
|
||||
var isLeaf: Bool {
|
||||
return childrens.isEmpty
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
//
|
||||
// SwiftrieStorableLogic.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 8.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SwiftrieStorableLogic: AnyObject {
|
||||
var root: SwiftriableNode { get set }
|
||||
var operations: OperationQueue { get set }
|
||||
var queue: DispatchQueue { get set }
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
import XCTest
|
||||
@testable import Swiftrie
|
||||
|
||||
final class SwiftrieTests: XCTestCase {
|
||||
|
||||
func testInitializing() {
|
||||
let count = 100
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
let items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
XCTAssertEqual(items.count, models.count)
|
||||
XCTAssertEqual(items.count, count)
|
||||
}
|
||||
|
||||
func testEmptyInitializing() {
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: [])
|
||||
let items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
XCTAssertEqual(items.count, 0)
|
||||
XCTAssertEqual(items.count, 0)
|
||||
}
|
||||
|
||||
func testOrdering() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
let items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
|
||||
for index in 0..<count {
|
||||
XCTAssertEqual(items[index].name, models[index].name)
|
||||
}
|
||||
}
|
||||
|
||||
func testInserting() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
var items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count, "counts are not equal")
|
||||
|
||||
let itemZero = SwiftrieTestModel(name: "item", id: 0)
|
||||
trie.insertItem(itemZero)
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count + 1, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count + 1, "counts are not equal")
|
||||
|
||||
XCTAssertEqual(
|
||||
itemZero.name, items.first?.name, "position is wrong (item should be added to head of list)"
|
||||
)
|
||||
|
||||
let itemSix = SwiftrieTestModel(name: "item 6", id: 6)
|
||||
trie.insertItem(itemSix)
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count + 2, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count + 2, "counts are not equal")
|
||||
|
||||
XCTAssertEqual(itemSix.name, items.last?.name, "position is wrong (item 6 added to end of list)")
|
||||
}
|
||||
|
||||
func testRemoving() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
var items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count, "counts are not equal")
|
||||
|
||||
let itemThree = models[2]
|
||||
trie.removeItem(itemThree)
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count - 1, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count - 1, "counts are not equal")
|
||||
|
||||
XCTAssertFalse(items.contains(where: {$0.name == itemThree.name}), "removed item is still in the list")
|
||||
|
||||
trie.insertItem(itemThree) // insert it again
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, models.count, "counts are not equal")
|
||||
XCTAssertEqual(items.count, count, "counts are not equal")
|
||||
|
||||
XCTAssertEqual(itemThree.name, items[2].name, "position is wrong (item 3 should be added at index 2)")
|
||||
|
||||
for index in 0..<count {
|
||||
XCTAssertEqual(items[index].name, models[index].name)
|
||||
}
|
||||
}
|
||||
|
||||
func testFailFinding() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
var findedResult: [SwiftrieTestModel] = [.init(name: "addedAItem", id: 0)]
|
||||
|
||||
let promise = expectation(description: "response-handler-fail-finding")
|
||||
trie.findItems(prefix: "wrong-prefix", throttle: 0, type: SwiftrieTestModel.self) { result in
|
||||
findedResult = result
|
||||
promise.fulfill()
|
||||
}
|
||||
wait(for: [promise], timeout: 2)
|
||||
|
||||
XCTAssertTrue(findedResult.count == 0)
|
||||
}
|
||||
|
||||
func testSuccessAllFinding() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
var findedResult: [SwiftrieTestModel] = []
|
||||
|
||||
let promise = expectation(description: "response-handler-success-all-finding")
|
||||
trie.findItems(prefix: "i", throttle: 0, type: SwiftrieTestModel.self) { result in
|
||||
findedResult = result
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
promise.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [promise], timeout: 2)
|
||||
|
||||
XCTAssertTrue(findedResult.count == 5)
|
||||
}
|
||||
|
||||
func testSuccessOneFinding() {
|
||||
let count = 5
|
||||
let models = TestModelCreator.get(count: count).models
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: models)
|
||||
|
||||
var findedResult: [SwiftrieTestModel] = []
|
||||
|
||||
let promise = expectation(description: "response-handler-success-one-finding")
|
||||
trie.findItems(prefix: "item 1", throttle: 0, type: SwiftrieTestModel.self) { result in
|
||||
findedResult = result
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
promise.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [promise], timeout: 2)
|
||||
|
||||
XCTAssertTrue(findedResult.count == 1)
|
||||
let items = findedResult.filter({$0.name.hasPrefix("item")})
|
||||
XCTAssertEqual(items.count, findedResult.count)
|
||||
}
|
||||
|
||||
// testing same word and different object
|
||||
func testDuplicationItems() {
|
||||
let item0 = SwiftrieTestModel(name: "same name", id: 0)
|
||||
let item1 = SwiftrieTestModel(name: "same name", id: 1)
|
||||
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: [item0, item1])
|
||||
|
||||
var items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
XCTAssertEqual(items.count, 2)
|
||||
|
||||
// remove it
|
||||
trie.removeItem(item0)
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, 1)
|
||||
|
||||
let hasContainItem2 = items[0].id == 1
|
||||
|
||||
XCTAssertTrue(hasContainItem2)
|
||||
|
||||
// insert it
|
||||
|
||||
trie.insertItem(item0)
|
||||
|
||||
items = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, 2)
|
||||
}
|
||||
|
||||
// testing inserting same
|
||||
func testInsertingSameItems() {
|
||||
let item0 = SwiftrieTestModel(name: "same name", id: 0)
|
||||
|
||||
let trie: SwiftrieAccessibleLogic = Swiftrie(swiftriables: [item0])
|
||||
|
||||
trie.insertItem(item0)
|
||||
|
||||
let items: [SwiftrieTestModel] = trie.getAllItems()
|
||||
|
||||
XCTAssertEqual(items.count, 1)
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
//
|
||||
// SwiftrieTestModel.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 9.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Swiftrie
|
||||
|
||||
struct SwiftrieTestModel {
|
||||
var name: String
|
||||
var id: Int
|
||||
}
|
||||
|
||||
extension SwiftrieTestModel: Swiftriable {
|
||||
var prefixableText: String {
|
||||
return name
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// TestModelCreator.swift
|
||||
//
|
||||
//
|
||||
// Created by okan.yucel on 9.03.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum TestModelCreator {
|
||||
case get(count: Int)
|
||||
|
||||
var models: [SwiftrieTestModel] {
|
||||
switch self {
|
||||
case .get(let count):
|
||||
var items: [SwiftrieTestModel] = []
|
||||
for index in 0..<count {
|
||||
items.append(.init(name: "item \(index)", id: index))
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue