Last active
July 2, 2023 11:05
-
-
Save woodycatliu/3a20c3a42b3699f935650049d8d46bb5 to your computer and use it in GitHub Desktop.
BindingValueSubject provides the ability to callback and update the source's new value.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BindingValueSubject.swift | |
// | |
// | |
// Created by Woody Liu on 2023/7/2. | |
// | |
import Combine | |
@frozen public struct BindingValue<Output>: Publisher { | |
public typealias Failure = Never | |
public func accept(_ input: Output) { | |
subject.send(input) | |
} | |
public var value: Output { | |
get { | |
subject.value | |
} | |
} | |
public init(_ value: Output) { | |
self.init(BindingValueSubject(value)) | |
} | |
public init(_ subject: CurrentValueSubject<Output, Failure>) { | |
self.init(BindingValueSubject(subject)) | |
} | |
public init(with publisher: AnyPublisher<Output, Failure>, value: Output, set: @escaping (Output) -> Void) { | |
let subject = BindingValueSubject(with: publisher, value: value, set: set) | |
self.init(subject) | |
} | |
public init(_ bindingValue: BindingValue<Output>) { | |
let s = bindingValue.subject | |
self.subject = s.child() | |
} | |
internal init(_ subject: BindingValueSubject<Output>) { | |
self.subject = subject | |
} | |
public func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Output == S.Input { | |
subject.receive(subscriber: subscriber) | |
} | |
private let subject: BindingValueSubject<Output> | |
} | |
// MARK: BindingValueSubject | |
public extension BindingValueSubject { | |
var binding: BindingValue<Output> { | |
return BindingValue(self) | |
} | |
} | |
final public class BindingValueSubject<Output>: Subject { | |
public typealias Failure = Never | |
final public var value: Output { | |
_get() | |
} | |
public convenience init(_ value: Output) { | |
let subject = CurrentValueSubject<Output, Failure>(value) | |
self.init(subject) | |
} | |
public convenience init(with publisher: AnyPublisher<Output, Failure>, value: Output, set: @escaping (Output) -> Void) { | |
let subject = CurrentValueSubject(with: publisher, value: value) | |
self.init( | |
set: set, | |
get: { | |
subject.value | |
}, | |
publisher: subject.eraseToAnyPublisher() | |
) | |
} | |
public convenience init(_ subject: CurrentValueSubject<Output, Failure>) { | |
self.init( | |
set: { value in | |
subject.send(value) | |
}, | |
get: { | |
subject.value | |
}, | |
publisher: subject.eraseToAnyPublisher() | |
) | |
} | |
public init(set: @escaping (Output) -> Void, | |
get: @escaping () -> Output, | |
publisher: AnyPublisher<Output, Failure>){ | |
self._set = set | |
self._get = get | |
self._publisher = publisher | |
} | |
public func send(_ input: Output) { | |
_set(input) | |
} | |
public func send(completion: Subscribers.Completion<Never>) {} | |
public func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Output == S.Input { | |
_publisher.receive(subscriber: subscriber) | |
} | |
public func send(subscription: Subscription) {} | |
private convenience init(_ subject: BindingValueSubject<Output>) { | |
self.init(set: subject._set, get: subject._get, publisher: subject._publisher) | |
} | |
private let _set: ((Output) -> Void) | |
private let _get: () -> Output | |
private let _publisher: AnyPublisher<Output, Failure> | |
} | |
extension BindingValueSubject { | |
fileprivate func child() -> BindingValueSubject<Output> { | |
return BindingValueSubject(self) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final class BindingValueSubjectTests: XCTestCase { | |
var value: BindingValueSubject<Int>! | |
var child: BindingValue<Int>! | |
var child2: BindingValue<Int>! | |
override func setUpWithError() throws { | |
value = BindingValueSubject<Int>(5) | |
child = value.binding | |
child2 = BindingValue(child) | |
} | |
override func tearDownWithError() throws { | |
value = nil | |
child = nil | |
child2 = nil | |
} | |
func testExample() async throws { | |
let newValue = try awaitPublisher(value.dropFirst().eraseToAnyPublisher(), timeout: 0.5, description: "") { | |
self.value.send(0) | |
} | |
XCTAssertEqual(newValue, 0) | |
XCTAssertEqual(child.value, newValue) | |
XCTAssertEqual(child2.value, 0) | |
let second = try awaitPublisher(value.dropFirst().eraseToAnyPublisher(), timeout: 0.5, description: "") { | |
self.child2.accept(1) | |
} | |
XCTAssertEqual(second, 1) | |
XCTAssertEqual(child.value, second) | |
XCTAssertEqual(child2.value, 1) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment