Skip to content

Commit

Permalink
latest version
Browse files Browse the repository at this point in the history
  • Loading branch information
Okan Yücel committed Sep 28, 2022
1 parent 5504bc8 commit 8c94f8b
Show file tree
Hide file tree
Showing 23 changed files with 1,059 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
77 changes: 77 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Swiftrie.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?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>
27 changes: 27 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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"])
]
)
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@

# 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()
```

### Swift Package Manager

To install CustomNavigationBar using [Swift Package Manager](https://github.com/apple/swift-package-manager) you can follow the [tutorial published by Apple](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) using the URL for the Swiftrie repo with the current version:

1. In Xcode, select “File” → “Swift Packages” → “Add Package Dependency”
1. Enter https://github.com/yucelokan/Swiftrie.git
22 changes: 22 additions & 0 deletions Sources/Swiftrie/Extensions/Codable+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// 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
}
}
60 changes: 60 additions & 0 deletions Sources/Swiftrie/Extensions/DispatchQueue+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// 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)
}
}
14 changes: 14 additions & 0 deletions Sources/Swiftrie/Extensions/String+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// 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))
}
}
63 changes: 63 additions & 0 deletions Sources/Swiftrie/OrderedDictionary/YOOrderedDictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// 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
}

}
12 changes: 12 additions & 0 deletions Sources/Swiftrie/Swiftriable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// SwiftrieItem.swift
//
//
// Created by okan.yucel on 5.03.2022.
//

import Foundation

public protocol Swiftriable: Codable {
var prefixableText: String { get }
}
Loading

0 comments on commit 8c94f8b

Please sign in to comment.