This commit is contained in:
Михаил Капелько
2023-12-16 11:00:13 +03:00
parent 769ade70c9
commit 1e36ef3d71
4 changed files with 72 additions and 134 deletions

View File

@@ -22,73 +22,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate
return true return true
} }
} }
/*
// Bus.Pipe.swift
public extension Bus {
final class BindingPipe<T: Equatable>: ObservableObject {
public let input = PassthroughSubject<T, Never>()
public let output = PassthroughSubject<T, Never>()
@Published var value: T
var isInput = false
var subscriptions = [AnyCancellable]()
deinit {
print("ИГР BusBP.DEinit(\(Unmanaged.passUnretained(self).toOpaque()))")
}
init(
_ defaultValue: T,
_ keyApp: String,
_ keyUI: String
) {
value = defaultValue
/**/dbg("ИГР BusBP.init(\(Unmanaged.passUnretained(self).toOpaque())) keyA: '\(keyApp)'")
// TextField -> output.
$value
.sink { [weak self] v in
assert(Thread.isMainThread)
// Ignore values received from input.
// Only accept UI provided values.
guard
let self,
!self.isInput
else {
return
}
self.output.send(v)
}
.store(in: &subscriptions)
// input -> TextField.
input
.sink { [weak self] v in
assert(Thread.isMainThread)
guard let self else { return }
/**/dbg("ИГР BusBP.init input: '\(v)'")
self.isInput = true
self.value = v
self.isInput = false
}
.store(in: &subscriptions)
// Оповещаем в мир об изменениях от UI.
Bus.sendAsync(
&subscriptions,
keyUI,
output.eraseToAnyPublisher()
)
Bus.receive(
&subscriptions,
[keyApp],
{ [weak self] _, v in
self?.input.send(v)
/**/print("ИГР BusBP.init receive: '\(v)'")
}
)
}
}
}
*/

View File

@@ -24,25 +24,6 @@ extension Bus {
} }
public extension Bus { public extension Bus {
static func receive<T>(
_ subscriptions: inout Set<AnyCancellable>,
_ keys: Set<String>,
_ handler: @escaping ((String, T) -> Void)
) {
Service.singleton?.broadcaster
.compactMap { v -> (String, T)? in
guard
keys.contains(v.key),
let value = v.value as? T
else {
return nil
}
return (v.key, value)
}
.sink { v in handler(v.0, v.1) }
.store(in: &subscriptions)
}
static func receiveAsync<T>( static func receiveAsync<T>(
_ subscriptions: inout Set<AnyCancellable>, _ subscriptions: inout Set<AnyCancellable>,
_ keys: Set<String>, _ keys: Set<String>,
@@ -63,13 +44,26 @@ public extension Bus {
.store(in: &subscriptions) .store(in: &subscriptions)
} }
static func send(_ key: String, _ value: Any) { static func receiveSync<T>(
Service.singleton?.send(key, value) _ subscriptions: inout Set<AnyCancellable>,
_ keys: Set<String>,
_ handler: @escaping ((String, T) -> Void)
) {
Service.singleton?.broadcaster
.compactMap { v -> (String, T)? in
guard
keys.contains(v.key),
let value = v.value as? T
else {
return nil
}
return (v.key, value)
}
.sink { v in handler(v.0, v.1) }
.store(in: &subscriptions)
} }
static func sendAsync<T>(
/*
static func sendAsync<T: Equatable>(
_ subscriptions: inout Set<AnyCancellable>, _ subscriptions: inout Set<AnyCancellable>,
_ key: String, _ key: String,
_ node: AnyPublisher<T, Never> _ node: AnyPublisher<T, Never>
@@ -79,5 +73,36 @@ public extension Bus {
.sink { v in Service.singleton?.send(key, v) } .sink { v in Service.singleton?.send(key, v) }
.store(in: &subscriptions) .store(in: &subscriptions)
} }
*/
static func sendSync<T>(
_ subscriptions: inout Set<AnyCancellable>,
_ key: String,
_ node: AnyPublisher<T, Never>
) {
node
.sink { v in Service.singleton?.send(key, v) }
.store(in: &subscriptions)
}
static func sendOnce(_ key: String, _ value: Any) {
Service.singleton?.send(key, value)
}
}
public extension Bus {
static func registerProcessing<Src, Dst>(
_ subscriptions: inout Set<AnyCancellable>,
_ keyIn: String,
_ keyOut: String,
_ handler: @escaping ((Src) -> Dst?)
) {
Service.singleton?.broadcaster
.filter { $0.key == keyIn }
.compactMap {
guard let vIn = $0.value as? Src else { return nil }
return handler(vIn)
}
.sink { vOut in Service.singleton?.send(keyOut, vOut) }
.store(in: &subscriptions)
}
} }

View File

@@ -18,6 +18,9 @@ enum MeetupId {
r += "-" r += "-"
} }
} }
if r.hasSuffix("-") {
r = String(r.dropLast(1))
}
return r return r
} }
@@ -29,36 +32,13 @@ enum MeetupId {
} }
init() { init() {
Bus.receive( Bus.registerProcessing(
&subscriptions, &subscriptions,
[Keys.meetupIdTextUI.rawValue], Keys.meetupIdTextUI.rawValue,
Self.handleFormatting Keys.meetupIdTextApp.rawValue,
MeetupId.shouldFormat
) )
/**/print("ИГР MeetupIF.init") /**/print("ИГР MeetupIF.init")
} }
static func handleFormatting(_: String, _ value: String) {
let out = MeetupId.shouldFormat(value)
/**/print("ИГР MeetupIF.handleF out/dt: '\(out)'/'\(Date())'")
Bus.send(Keys.meetupIdTextApp.rawValue, out)
} }
} }
/*
struct V: View {
/*@StateObject*/ var fmt = MeetupIdFormatter()
@StateObject var txt = Bus.BindingPipe("", Keys.meetupIdTextApp.rawValue, Keys.meetupIdTextUI.rawValue)
var body: some View {
VStack {
Text("Hi, the text is: '\(txt.value)'")
.fontWeight(.bold)
TextField("Placeholder", text: $txt.value)
.padding(8)
.border(Color.blue, width: 2)
}
.frame(width: 320)
.padding()
}
}
*/
}

View File

@@ -7,6 +7,9 @@ final class VM: ObservableObject {
var subscriptions = Set<AnyCancellable>() var subscriptions = Set<AnyCancellable>()
init() { init() {
Bus.sendSync(
&subscriptions,
MeetupId.Keys.meetupIdTextUI.rawValue,
$text $text
// Исключаем конфликты от UI и App путём игнорирования спама. // Исключаем конфликты от UI и App путём игнорирования спама.
.debounce(for: .seconds(0.3), scheduler: DispatchQueue.main) .debounce(for: .seconds(0.3), scheduler: DispatchQueue.main)
@@ -14,8 +17,8 @@ final class VM: ObservableObject {
.filter { $0.hasPrefix("u:") } .filter { $0.hasPrefix("u:") }
// Убираем источник. // Убираем источник.
.map { String($0.dropFirst(2)) } .map { String($0.dropFirst(2)) }
.sink { Bus.send(MeetupId.Keys.meetupIdTextUI.rawValue, $0) } .eraseToAnyPublisher()
.store(in: &subscriptions) )
Bus.receiveAsync( Bus.receiveAsync(
&subscriptions, &subscriptions,