Files
damus/damus/Models/NostrNetworkManager/SubscriptionManager.swift
T
Daniel D’Aquino 3a0acfaba1 Implement NostrNetworkManager and UserRelayListManager
This commit implements a new layer called NostrNetworkManager,
responsible for managing interactions with the Nostr network, and
providing a higher level API that is easier and more secure to use for
the layer above it.

It also integrates it with the rest of the app, by moving RelayPool and PostBox
into NostrNetworkManager, along with all their usages.

Changelog-Added: Added NIP-65 relay list support
Changelog-Changed: Improved robustness of relay list handling
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
2025-04-16 11:48:52 -07:00

71 lines
3.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// SubscriptionManager.swift
// damus
//
// Created by Daniel DAquino on 2025-03-25.
//
extension NostrNetworkManager {
/// Reads or fetches information from RelayPool and NostrDB, and provides an easier and unified higher-level interface.
///
/// ## Implementation notes
///
/// - This class will be a key part of the local relay model migration. Most higher-level code should fetch content from this class, which will properly setup the correct relay pool subscriptions, and provide a stream from NostrDB for higher performance and reliability.
class SubscriptionManager {
private let pool: RelayPool
private var ndb: Ndb
init(pool: RelayPool, ndb: Ndb) {
self.pool = pool
self.ndb = ndb
}
// MARK: - Reading data from Nostr
/// Subscribes to data from the user's relays
///
/// ## Implementation notes
///
/// - When we migrate to the local relay model, we should modify this function to stream directly from NostrDB
///
/// - Parameter filters: The nostr filters to specify what kind of data to subscribe to
/// - Returns: An async stream of nostr data
func subscribe(filters: [NostrFilter]) -> AsyncStream<StreamItem> {
return AsyncStream<StreamItem> { continuation in
let streamTask = Task {
for await item in self.pool.subscribe(filters: filters) {
switch item {
case .eose: continuation.yield(.eose)
case .event(let nostrEvent):
// At this point of the pipeline, if the note is valid it should have been processed and verified by NostrDB,
// in which case we should pull the note from NostrDB to ensure validity.
// However, NdbNotes are unowned, so we return a function where our callers can temporarily borrow the NostrDB note
let noteId = nostrEvent.id
let lender: NdbNoteLender = { lend in
guard let ndbNoteTxn = self.ndb.lookup_note(noteId) else {
throw NdbNoteLenderError.errorLoadingNote
}
guard let unownedNote = UnownedNdbNote(ndbNoteTxn) else {
throw NdbNoteLenderError.errorLoadingNote
}
lend(unownedNote)
}
continuation.yield(.event(borrow: lender))
}
}
}
continuation.onTermination = { @Sendable _ in
streamTask.cancel() // Close the RelayPool stream when caller stops streaming
}
}
}
}
enum StreamItem {
/// An event which can be borrowed from NostrDB
case event(borrow: NdbNoteLender)
/// The end of stored events
case eose
}
}