@@ -0,0 +1 @@ | |||||
../../../../u/Modules/BusX |
@@ -1,31 +0,0 @@ | |||||
import Combine | |||||
public extension Bus { | |||||
final class Async<Src, Dst> { | |||||
let v = PassthroughSubject<Src, Never>() | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ handler: @escaping ((Src) -> Dst?), | |||||
_ src: String, | |||||
_ dst: String | |||||
) { | |||||
// Вход. | |||||
Bus.receiveSync( | |||||
[src], | |||||
{ [weak self] _, v in self?.v.send(v) }, | |||||
&subscriptions | |||||
) | |||||
// Выход. | |||||
Bus.sendSync( | |||||
dst, | |||||
v | |||||
.compactMap { (v: Src) in handler(v) } | |||||
// Асинхронно. | |||||
.receive(on: DispatchQueue.main) | |||||
.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
public extension Bus { | |||||
/// Пропускаем далее единственный ключ. | |||||
static func convertKeyValue<T>( | |||||
_ key: String, | |||||
_ v: (key: String, value: Any) | |||||
) -> (String, T)? { | |||||
guard | |||||
key == v.key, | |||||
let value = v.value as? T | |||||
else { | |||||
return nil | |||||
} | |||||
return (key, value) | |||||
} | |||||
/// Пропускаем далее множество ключей. | |||||
static func convertKeyValue<T>( | |||||
_ keys: Set<String>, | |||||
_ v: (key: String, value: Any) | |||||
) -> (String, T)? { | |||||
guard | |||||
keys.contains(v.key), | |||||
let value = v.value as? T | |||||
else { | |||||
return nil | |||||
} | |||||
return (v.key, value) | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
import Combine | |||||
public extension Bus { | |||||
final class Debounce<Src, Dst> { | |||||
let v = PassthroughSubject<Src, Never>() | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ handler: @escaping ((Src) -> Dst?), | |||||
_ sec: Double, | |||||
_ src: String, | |||||
_ dst: String | |||||
) { | |||||
// Вход. | |||||
Bus.receiveSync( | |||||
[src], | |||||
{ [weak self] _, v in self?.v.send(v) }, | |||||
&subscriptions | |||||
) | |||||
// Выход. | |||||
Bus.sendSync( | |||||
dst, | |||||
v | |||||
.debounce(for: .seconds(sec), scheduler: DispatchQueue.main) | |||||
.compactMap { (v: Src) in handler(v) } | |||||
.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,31 +0,0 @@ | |||||
import Combine | |||||
public extension Bus { | |||||
final class Delay<Src, Dst> { | |||||
let v = PassthroughSubject<Src, Never>() | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ handler: @escaping ((Src) -> Dst?), | |||||
_ sec: Double, | |||||
_ src: String, | |||||
_ dst: String | |||||
) { | |||||
// Вход. | |||||
Bus.receiveSync( | |||||
[src], | |||||
{ [weak self] _, v in self?.v.send(v) }, | |||||
&subscriptions | |||||
) | |||||
// Выход. | |||||
Bus.sendSync( | |||||
dst, | |||||
v | |||||
.delay(for: .seconds(sec), scheduler: DispatchQueue.main) | |||||
.compactMap { (v: Src) in handler(v) } | |||||
.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
import Combine | |||||
public extension Bus { | |||||
final class Sync<Src, Dst> { | |||||
let v = PassthroughSubject<Src, Never>() | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ handler: @escaping ((Src) -> Dst?), | |||||
_ src: String, | |||||
_ dst: String | |||||
) { | |||||
// Вход. | |||||
Bus.receiveSync( | |||||
[src], | |||||
{ [weak self] _, v in self?.v.send(v) }, | |||||
&subscriptions | |||||
) | |||||
// Выход. | |||||
Bus.sendSync( | |||||
dst, | |||||
v | |||||
.compactMap { (v: Src) in handler(v) } | |||||
.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,56 +0,0 @@ | |||||
import Combine | |||||
import Foundation | |||||
public enum Bus { | |||||
private static let e = PassthroughSubject<(key: String, value: Any), Never>() | |||||
public static var events: AnyPublisher<(key: String, value: Any), Never> { | |||||
e.eraseToAnyPublisher() | |||||
} | |||||
} | |||||
public extension Bus { | |||||
/// Асинхронно обрабатываем входящие события из шины. | |||||
static func receiveAsync<T>( | |||||
_ keys: Set<String>, | |||||
_ handler: @escaping ((String, T) -> Void), | |||||
_ subscriptions: inout [AnyCancellable] | |||||
) { | |||||
e | |||||
.compactMap { convertKeyValue(keys, $0) } | |||||
.receive(on: DispatchQueue.main) | |||||
.sink { v in handler(v.0, v.1) } | |||||
.store(in: &subscriptions) | |||||
} | |||||
/// Синхронно обрабатываем входящие события из шины. | |||||
static func receiveSync<T>( | |||||
_ keys: Set<String>, | |||||
_ handler: @escaping ((String, T) -> Void), | |||||
_ subscriptions: inout [AnyCancellable] | |||||
) { | |||||
e | |||||
.compactMap { convertKeyValue(keys, $0) } | |||||
.sink { v in handler(v.0, v.1) } | |||||
.store(in: &subscriptions) | |||||
} | |||||
/// Синхронно отправляем события из узла в шину. | |||||
/// | |||||
/// Для асинхронной отправки достаточно добавить оператор `receive(on:)` | |||||
/// в цепочке параметра `node` | |||||
static func sendSync<T>( | |||||
_ key: String, | |||||
_ node: AnyPublisher<T, Never>, | |||||
_ subscriptions: inout [AnyCancellable] | |||||
) { | |||||
node | |||||
.sink { v in e.send((key, v)) } | |||||
.store(in: &subscriptions) | |||||
} | |||||
/// Единоразово синхронно отправляем событие в шину. | |||||
static func send(_ key: String, _ value: Any) { | |||||
e.send((key, value)) | |||||
} | |||||
} |
@@ -1,9 +0,0 @@ | |||||
extension BusUI { | |||||
/// Пропускаем лишь значения от UI | |||||
/// | |||||
/// - Returns: Значение без префиксов "a:"/"u:" | |||||
static func onlyUIText(_ s: String) -> String? { | |||||
guard s.hasPrefix("u:") else { return nil } | |||||
return String(s.dropFirst(2)) | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
import Combine | |||||
extension BusUI { | |||||
public final class Button: ObservableObject { | |||||
public let v = PassthroughSubject<Void, Never>() | |||||
var subscriptions = [AnyCancellable]() | |||||
public init(_ key: String) { | |||||
Bus.sendSync( | |||||
key, | |||||
v.map { true }.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,29 +0,0 @@ | |||||
import Combine | |||||
import SwiftUI | |||||
extension BusUI { | |||||
public final class TextField: ObservableObject { | |||||
@Published public var v = "a:" | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ textApp: String, | |||||
_ textUI: String | |||||
) { | |||||
Bus.sendSync( | |||||
textUI, | |||||
$v | |||||
.removeDuplicates() | |||||
.compactMap(onlyUIText) | |||||
.eraseToAnyPublisher(), | |||||
&subscriptions | |||||
) | |||||
Bus.receiveSync( | |||||
[textApp], | |||||
{ [weak self] (_, v: String) in self?.v = "a:\(v)" }, | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1,22 +0,0 @@ | |||||
import Foundation | |||||
extension BusUI { | |||||
public final class TextFieldSource: Formatter { | |||||
/// Выдаём для отображения очищенную от источника строку. | |||||
public override func string(for obj: Any?) -> String? { | |||||
guard let str = obj as? String else { return nil } | |||||
return String(str.dropFirst(2)) | |||||
} | |||||
/// Выдаём для использования кодом строку с указанием источника | |||||
/// в виде пользователя `u:` | |||||
public override func getObjectValue( | |||||
_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, | |||||
for string: String, | |||||
errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>? | |||||
) -> Bool { | |||||
obj?.pointee = "u:\(string)" as AnyObject | |||||
return true | |||||
} | |||||
} | |||||
} |
@@ -1,20 +0,0 @@ | |||||
import Combine | |||||
extension BusUI { | |||||
public final class Value<T>: ObservableObject { | |||||
@Published public var v: T | |||||
var subscriptions = [AnyCancellable]() | |||||
public init( | |||||
_ key: String, | |||||
_ defaultValue: T | |||||
) { | |||||
v = defaultValue | |||||
Bus.receiveSync( | |||||
[key], | |||||
{ [weak self] (_, v: T) in self?.v = v }, | |||||
&subscriptions | |||||
) | |||||
} | |||||
} | |||||
} |
@@ -1 +0,0 @@ | |||||
public enum BusUI { } |
@@ -1,14 +0,0 @@ | |||||
Pod::Spec.new do |s| | |||||
s.name = 'BusX' | |||||
s.version = '2023.12.30' | |||||
s.license = 'IVCS' | |||||
s.summary = 'Шина общения элементов приложения' | |||||
s.homepage = 'IVCS' | |||||
s.author = 'IVCS' | |||||
s.source = { :git => 'https://fake.com/FAKE.git', :tag => s.version } | |||||
s.source_files = '**/**/*.swift' | |||||
s.swift_version = '5.2' | |||||
s.ios.deployment_target = '14.0' | |||||
end |
@@ -1,6 +1,6 @@ | |||||
PODS: | PODS: | ||||
- AELog (0.6.3) | - AELog (0.6.3) | ||||
- BusX (2023.12.30) | |||||
- BusX (2024.01.15) | |||||
- MeetupIdX (2023.12.31): | - MeetupIdX (2023.12.31): | ||||
- AELog | - AELog | ||||
- BusX | - BusX | ||||
@@ -27,7 +27,7 @@ EXTERNAL SOURCES: | |||||
SPEC CHECKSUMS: | SPEC CHECKSUMS: | ||||
AELog: f732b70f7a9d1b4c6a3676304192b3908f362133 | AELog: f732b70f7a9d1b4c6a3676304192b3908f362133 | ||||
BusX: d11e857e9cb762f649ee9f88fd5a4f8fbf5bf96b | |||||
BusX: 1db5cf8652f7b206af468cc115cab3326efd1ced | |||||
MeetupIdX: 2fa9fb27717aa8878ff495c1abe960c96e524308 | MeetupIdX: 2fa9fb27717aa8878ff495c1abe960c96e524308 | ||||
MPAKX: dc592434f55edf34709f6e4f37c9ec90dcd95185 | MPAKX: dc592434f55edf34709f6e4f37c9ec90dcd95185 | ||||