@@ -22,73 +22,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate | |||
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)'") | |||
} | |||
) | |||
} | |||
} | |||
} | |||
*/ |
@@ -24,7 +24,7 @@ extension Bus { | |||
} | |||
public extension Bus { | |||
static func receive<T>( | |||
static func receiveAsync<T>( | |||
_ subscriptions: inout Set<AnyCancellable>, | |||
_ keys: Set<String>, | |||
_ handler: @escaping ((String, T) -> Void) | |||
@@ -39,11 +39,12 @@ public extension Bus { | |||
} | |||
return (v.key, value) | |||
} | |||
.receive(on: DispatchQueue.main) | |||
.sink { v in handler(v.0, v.1) } | |||
.store(in: &subscriptions) | |||
} | |||
static func receiveAsync<T>( | |||
static func receiveSync<T>( | |||
_ subscriptions: inout Set<AnyCancellable>, | |||
_ keys: Set<String>, | |||
_ handler: @escaping ((String, T) -> Void) | |||
@@ -58,18 +59,11 @@ public extension Bus { | |||
} | |||
return (v.key, value) | |||
} | |||
.receive(on: DispatchQueue.main) | |||
.sink { v in handler(v.0, v.1) } | |||
.store(in: &subscriptions) | |||
} | |||
static func send(_ key: String, _ value: Any) { | |||
Service.singleton?.send(key, value) | |||
} | |||
/* | |||
static func sendAsync<T: Equatable>( | |||
static func sendAsync<T>( | |||
_ subscriptions: inout Set<AnyCancellable>, | |||
_ key: String, | |||
_ node: AnyPublisher<T, Never> | |||
@@ -79,5 +73,36 @@ public extension Bus { | |||
.sink { v in Service.singleton?.send(key, v) } | |||
.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) | |||
} | |||
} |
@@ -18,6 +18,9 @@ enum MeetupId { | |||
r += "-" | |||
} | |||
} | |||
if r.hasSuffix("-") { | |||
r = String(r.dropLast(1)) | |||
} | |||
return r | |||
} | |||
@@ -29,36 +32,13 @@ enum MeetupId { | |||
} | |||
init() { | |||
Bus.receive( | |||
&subscriptions, | |||
[Keys.meetupIdTextUI.rawValue], | |||
Self.handleFormatting | |||
Bus.registerProcessing( | |||
&subscriptions, | |||
Keys.meetupIdTextUI.rawValue, | |||
Keys.meetupIdTextApp.rawValue, | |||
MeetupId.shouldFormat | |||
) | |||
/**/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() | |||
} | |||
} | |||
*/ | |||
} |
@@ -7,15 +7,18 @@ final class VM: ObservableObject { | |||
var subscriptions = Set<AnyCancellable>() | |||
init() { | |||
$text | |||
// Исключаем конфликты от UI и App путём игнорирования спама. | |||
.debounce(for: .seconds(0.3), scheduler: DispatchQueue.main) | |||
// Нужны лишь значения от UI. | |||
.filter { $0.hasPrefix("u:") } | |||
// Убираем источник. | |||
.map { String($0.dropFirst(2)) } | |||
.sink { Bus.send(MeetupId.Keys.meetupIdTextUI.rawValue, $0) } | |||
.store(in: &subscriptions) | |||
Bus.sendSync( | |||
&subscriptions, | |||
MeetupId.Keys.meetupIdTextUI.rawValue, | |||
$text | |||
// Исключаем конфликты от UI и App путём игнорирования спама. | |||
.debounce(for: .seconds(0.3), scheduler: DispatchQueue.main) | |||
// Нужны лишь значения от UI. | |||
.filter { $0.hasPrefix("u:") } | |||
// Убираем источник. | |||
.map { String($0.dropFirst(2)) } | |||
.eraseToAnyPublisher() | |||
) | |||
Bus.receiveAsync( | |||
&subscriptions, | |||