Skip to content

Commit

Permalink
SwiftUI initial work. More documentation to come. (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marco Arment committed Dec 7, 2022
1 parent 738bb38 commit c31ba30
Show file tree
Hide file tree
Showing 13 changed files with 1,191 additions and 3 deletions.
82 changes: 82 additions & 0 deletions Sources/Blackbird/BlackbirdChanges.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,34 @@ public extension Blackbird {
///
typealias ChangePublisher = PassthroughSubject<PrimaryKeyValues?, Never>

internal static func isRelevantPrimaryKeyChange(watchedPrimaryKeys: Blackbird.PrimaryKeyValues?, changedPrimaryKeys: Blackbird.PrimaryKeyValues?) -> Bool {
guard let watchedPrimaryKeys else {
// Not watching any particular keys -- always update for any table change
return true
}

guard let changedPrimaryKeys else {
// Change sent for unknown/all keys -- always update
return true
}

if !watchedPrimaryKeys.intersection(changedPrimaryKeys).isEmpty {
// Overlapping keys -- update
return true
}

return false
}

// MARK: - Legacy notifications

static let legacyChangeNotification = NSNotification.Name("BlackbirdTableChangeNotification")
static let legacyChangeNotificationTableKey = "BlackbirdChangedTable"
static let legacyChangeNotificationPrimaryKeyValuesKey = "BlackbirdChangedPrimaryKeyValues"
}

// MARK: - Change publisher

extension Blackbird.Database {
internal class ChangeReporter {
private var lock = Blackbird.Lock()
Expand Down Expand Up @@ -159,3 +182,62 @@ extension Blackbird.Database {
}
}
}

// MARK: - General query cache with Combine publisher

extension Blackbird {
public typealias CachedResultGenerator<T> = ((_ db: Blackbird.Database) async throws -> T)

public class CachedResultPublisher<T> {
public var valuePublisher = CurrentValueSubject<T?, Never>(nil)

private var cacheLock = Lock()
private var cachedResults: T? = nil

private var tableName: String? = nil
private var database: Blackbird.Database? = nil
private var generator: CachedResultGenerator<T>? = nil
private var tableChangePublisher: AnyCancellable? = nil

public func subscribe(to tableName: String, in database: Blackbird.Database?, generator: CachedResultGenerator<T>?) {
self.tableName = tableName
self.generator = generator
self.changeDatabase(database)
enqueueUpdate()
}

private func update(_ cachedResults: T?) async throws {
let results: T?
if let cachedResults {
results = cachedResults
} else {
results = (generator != nil && database != nil ? try await generator!(database!) : nil)
cacheLock.withLock { self.cachedResults = results }
await MainActor.run {
valuePublisher.send(results)
}
}
}

private func changeDatabase(_ newDatabase: Database?) {
if newDatabase == database { return }
database = newDatabase
self.cacheLock.withLock { self.cachedResults = nil }

if let database, let tableName {
self.tableChangePublisher = database.changeReporter.changePublisher(for: tableName).sink { [weak self] _ in
guard let self else { return }
self.cacheLock.withLock { self.cachedResults = nil }
self.enqueueUpdate()
}
} else {
self.tableChangePublisher = nil
}
}

private func enqueueUpdate() {
let cachedResults = cacheLock.withLock { self.cachedResults }
Task { do { try await self.update(cachedResults) } catch { print("[Blackbird.ModelArrayUpdater<\(String(describing: T.self))>] ⚠️ Error updating: \(error.localizedDescription)") } }
}
}
}
2 changes: 1 addition & 1 deletion Sources/Blackbird/BlackbirdDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ extension Blackbird {
internal let perfLog: PerformanceLogger

private static var instanceLock = Lock()
private static var nextInstanceID: InstanceID = 0
private static var nextInstanceID: InstanceID = 1
private static var pathsOfCurrentInstances = Set<String>()

private var isClosedLock = Lock()
Expand Down
8 changes: 6 additions & 2 deletions Sources/Blackbird/BlackbirdModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,16 @@ public protocol BlackbirdModel: Codable, Equatable, Identifiable {
/// - Returns: An array of rows matching the query if applicable, or an empty array otherwise.
@discardableResult static func query(in database: Blackbird.Database, _ query: String, arguments: [String: Any]) async throws -> [Blackbird.Row]

// -- Listening for changes --

/// The change publisher for this model's table.
/// - Parameter database: The ``Blackbird/Database`` instance to monitor.
/// - Returns: The ``Blackbird/ChangePublisher`` for this model's table.
static func changePublisher(in database: Blackbird.Database) -> Blackbird.ChangePublisher

/// Shorthand for this type's `ModelArrayUpdater` interface for SwiftUI.
typealias ArrayUpdater = Blackbird.ModelArrayUpdater<Self>

/// Shorthand for this type's `ModelInstanceUpdater` interface for SwiftUI.
typealias InstanceUpdater = Blackbird.ModelInstanceUpdater<Self>
}

extension BlackbirdModel {
Expand Down
Loading

0 comments on commit c31ba30

Please sign in to comment.