Skip to content

Commit ce21fbf

Browse files
committed
Initial commit
0 parents  commit ce21fbf

14 files changed

+606
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Package.resolved

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "BarcelonaFoundation",
8+
platforms: [
9+
.macOS(.v10_14), .iOS(.v13)
10+
],
11+
products: [
12+
.library(
13+
name: "BarcelonaFoundation",
14+
targets: ["BarcelonaFoundation"]),
15+
],
16+
dependencies: [
17+
.package(url: "https://github.com/EricRabil/Pwomise", .upToNextMajor(from: "1.0.3")),
18+
.package(url: "https://github.com/EricRabil/Swexy", .upToNextMajor(from: "1.0.1")),
19+
.package(url: "https://github.com/EricRabil/Swog", .branch("main"))
20+
],
21+
targets: [
22+
.target(
23+
name: "BarcelonaFoundation",
24+
dependencies: ["Pwomise", "Swexy", "Swog"])
25+
]
26+
)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# BarcelonaFoundation
2+
3+
A description of this package.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// BarcelonaFoundation.swift
3+
// BarcelonaFoundation
4+
//
5+
// Created by Eric Rabil on 7/31/21.
6+
// Copyright © 2021 Eric Rabil. All rights reserved.
7+
//
8+
9+
import Foundation
10+
@_exported import Swexy
11+
@_exported import Pwomise
12+
@_exported import Swog
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// Color.swift
3+
// BarcelonaFoundation
4+
//
5+
// Created by Eric Rabil on 7/24/21.
6+
// Copyright © 2021 Eric Rabil. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import CoreGraphics
11+
12+
public struct Color: Codable, Hashable {
13+
let red: CGFloat
14+
let blue: CGFloat
15+
let green: CGFloat
16+
let alpha: CGFloat
17+
}
18+
19+
public struct DynamicColor: Codable, Hashable {
20+
let light: Color?
21+
let dark: Color?
22+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// Debouncer.swift
3+
// CoreBarcelona
4+
//
5+
// Created by Eric Rabil on 8/18/20.
6+
// Copyright © 2020 Eric Rabil. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// Fires a callback after the call function has not been called for the specified delay
12+
open class Debouncer {
13+
/// The duration in which calls will be throttled
14+
var delay: Double
15+
16+
/// The timer being tracked by the debouncer
17+
weak var timer: Timer?
18+
19+
public init(delay: Double) {
20+
self.delay = delay
21+
}
22+
23+
/// Fires a callback function if and only if there are no subsequent calls during the debounce period
24+
/// - Parameter callback: callback function to call
25+
public func call(_ callback: @escaping () -> ()) {
26+
timer?.invalidate()
27+
28+
let nextTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { _ in
29+
callback()
30+
}
31+
32+
timer = nextTimer
33+
}
34+
}
35+
36+
/// Manages a set of debouncers associated with a string
37+
open class IdentifiableDebounceManager {
38+
/// Debouncers for the given identifiers
39+
private var debouncers: [String: Debouncer] = [:]
40+
41+
/// The duration in which calls for a given identifier will be throttled
42+
private let delay: Double
43+
44+
public init(_ delay: Double) {
45+
self.delay = delay
46+
}
47+
48+
/// Returns or constructs a debouncer for the given id
49+
/// - Parameter id: id to resolve
50+
/// - Returns: debouncer object
51+
private func debouncer(forID id: String) -> Debouncer {
52+
if let debouncer = debouncers[id] {
53+
return debouncer
54+
}
55+
56+
debouncers[id] = Debouncer(delay: delay)
57+
58+
return debouncers[id]!
59+
}
60+
61+
/// Drops a debouncer from storage
62+
/// - Parameter id: identifier of the debouncer to clear
63+
private func clearDebouncer(forID id: String) {
64+
if let index = debouncers.index(forKey: id) {
65+
debouncers.remove(at: index)
66+
}
67+
}
68+
69+
/// Submits a task to an identifiable debouncer
70+
/// - Parameters:
71+
/// - id: identifier of the debouncer to call
72+
/// - cb: task to execute if conditions are met
73+
public func submit(id: String, cb: @escaping () -> ()) {
74+
debouncer(forID: id).call {
75+
RunLoop.main.schedule {
76+
self.clearDebouncer(forID: id)
77+
}
78+
79+
cb()
80+
}
81+
}
82+
}
83+
84+
/// Manages a set of managers with the specified categories
85+
open class CategorizedDebounceManager<P: Hashable> {
86+
/// Ledger of category to debounce manager
87+
private var debouncers: [P: IdentifiableDebounceManager] = [:]
88+
89+
/// Default delay to use for debounce managers
90+
private let defaultDelay: Double = 1 / 10
91+
92+
/// Constructs a categorized debounce manager
93+
/// - Parameter configuration: ledger of debounce categories to delays
94+
public init(_ configuration: [P: Double]) {
95+
configuration.forEach {
96+
debouncers[$0.key] = .init($0.value)
97+
}
98+
}
99+
100+
/// Returns or constructs a debouncer for the given category
101+
/// - Parameter category: category of the debouncer to resolve
102+
/// - Returns: a debounce manager
103+
private func debouncer(for category: P) -> IdentifiableDebounceManager {
104+
if let debouncer = debouncers[category] {
105+
return debouncer
106+
}
107+
108+
debouncers[category] = .init(defaultDelay)
109+
110+
return debouncers[category]!
111+
}
112+
113+
/// Submits a task to a debouncer on a given category
114+
/// - Parameters:
115+
/// - id: identifier of the debouncer
116+
/// - category: category of the debouncer
117+
/// - cb: task to execute if conditions are met
118+
public func submit(_ id: String, category: P, cb: @escaping () -> ()) {
119+
debouncer(for: category).submit(id: id, cb: cb)
120+
}
121+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// ExpiringCollection.swift
3+
// BarcelonaFoundation
4+
//
5+
// Created by Eric Rabil on 7/26/21.
6+
// Copyright © 2021 Eric Rabil. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import OSLog
11+
12+
public class ExpiringCollection<Element: Hashable>: Collection {
13+
public private(set) var inner = Set<Element>()
14+
15+
public typealias Index = Set<Element>.Index
16+
17+
public let threshold: TimeInterval = 60
18+
public var runLoop: RunLoop = .current
19+
20+
private var expirationTimers = [Element: Timer]()
21+
22+
public var startIndex: Set<Element>.Index { inner.startIndex }
23+
public var endIndex: Set<Element>.Index { inner.endIndex }
24+
25+
public init() {
26+
27+
}
28+
29+
public func insert(_ item: Element) {
30+
inner.insert(item)
31+
expirationTimers[item]?.invalidate()
32+
33+
runLoop.schedule {
34+
self.expirationTimers[item] = Timer.scheduledTimer(withTimeInterval: self.threshold, repeats: false) { timer in
35+
self.expirationTimers[item] = nil
36+
self.inner.remove(item)
37+
}
38+
}
39+
}
40+
41+
public func remove(_ item: Element) {
42+
inner.remove(item)
43+
expirationTimers[item]?.invalidate()
44+
}
45+
46+
public subscript(index: Set<Element>.Index) -> Element {
47+
inner[index]
48+
}
49+
50+
public func index(after i: Set<Element>.Index) -> Set<Element>.Index {
51+
inner.index(after: i)
52+
}
53+
}

0 commit comments

Comments
 (0)