CombineCommunity / RxCombine

Bi-directional type bridging between RxSwift and Apple's Combine framework
MIT License
1.03k stars 86 forks source link

New replaceError(with:) bug #14

Closed BrianDoig closed 4 years ago

BrianDoig commented 4 years ago

A second issue showed up with replaceError(with:) in a more complicated piece of code.

extension ObservableConvertibleType {

    /// Returns a `AnyPublisher` of the underlying Observable's Element type
    /// so the Observable pushes events to the Publisher.
    ///
    /// - returns: AnyPublisher of the underlying Observable's Element type.
    /// - note: This is a work around for a crash when using replaceError(with:)
    public func asPublisherReplaceError(with: Element) -> AnyPublisher<Self.Element, Never> {
        return self  // NOTE: This crashes
            .publisher
            .replaceError(with: with)
            .eraseToAnyPublisher()
//        return self  // NOTE: This works
//            .publisher
//            .catch({ _ -> AnyPublisher<Self.Element, Never> in
//                Just(with).eraseToAnyPublisher()
//            })
//            .eraseToAnyPublisher()
    }
}

class Bound<Value>: ObservableObject {
    let bag = DisposeBag()

    var value: Value {
        didSet {
            didChange.send(value)
        }
    }

    let didChange = PassthroughSubject<Value, Never>()

    init(_ value: Value) { self.value = value }
}

extension Observable {
    func asBindableObject(initialValue: Element) -> Bound<Element> {
        let bound = Bound(initialValue)
        subscribe(onNext: { [weak bound] in bound?.value = $0 }).disposed(by: bound.bag)
        return bound
    }
}

class PublishedBehaviorRelay<T> {
    typealias PublishType = AnyPublisher<T, Never>
    typealias BindableType = Bound<T>

    let dataSource: BehaviorRelay<T>
    var bindable: BindableType {
        return dataSource
            .asObservable()
            .asBindableObject(initialValue: dataSource.value)
    }
    var publisher: PublishType {
        return dataSource
            .asPublisherReplaceError(with: dataSource.value)
    }

    init(initialValue: T) {
        dataSource = BehaviorRelay(value: initialValue)
    }
}

class UserViewModel: ObservableObject {
    @Published var sliderValue = 0.0
    @Published var rxswift = "Boo Hiss"

    var sliderString: String {
        return "\(sliderValue)"
    }
}

struct ContentView: View {

    @ObservedObject private var userViewModel: UserViewModel

    let relay = PublishedBehaviorRelay(initialValue: "Start")

    var cancelables: Set<AnyCancellable> = []
    let disposeBag = DisposeBag()

    init() {
        let uvm = UserViewModel()
        userViewModel = uvm

        // A publisher publishing numbers
        let data = 0...1000
        data
            .publisher
            .map({ "\($0)" })
            .modulated(0.01, bufferSize: data.count, scheduler: RunLoop.main)
            .eraseToAnyPublisher()
            .asObservable()
            .bind(to: relay.dataSource)
            .disposed(by: disposeBag
        )

        // CRASH ON THIS LINE OF CODE
        relay.publisher.assign(to: \.rxswift, on: uvm).store(in: &cancelables) 

    }

    var body: some View {
        VStack {
            Slider(value: $userViewModel.sliderValue)
                .padding(.horizontal, 40.0)
            VStack {
                Text("Hello, SwiftUI!")
                    .font(.largeTitle)
                    .foregroundColor(.green)
                    .padding(0.0)
                Text(userViewModel.sliderString)
                    .font(.title)
                    .foregroundColor(Color.black)
                    .multilineTextAlignment(.center)
                Text(userViewModel.rxswift)
            }
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This code works fine with the commented out asPublisherReplaceError(with:) but crashes in a different spot than it did before version 1.5 with the current implementation of asPublisherReplaceError(with:).

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x00007fff233b167a Combine`Combine.Publishers.ReplaceError.(Inner in _50EC2DA41500D4F648F58C2EE8939789).receive(A.Output) -> Combine.Subscribers.Demand + 314
    frame #1: 0x00007fff233b1b70 Combine`protocol witness for Combine.Subscriber.receive(A.Input) -> Combine.Subscribers.Demand in conformance Combine.Publishers.ReplaceError<A>.(Inner in _50EC2DA41500D4F648F58C2EE8939789)<A1> : Combine.Subscriber in Combine + 16
    frame #2: 0x0000000103ba2ae6 RxCombine`Subscriber<>.pushRxEvent(event=RxSwift.Event<τ_0_0.Input> @ scalar, self=<no summary available>) at Subscriber+Rx.swift:35:17
    frame #3: 0x0000000103ba05e4 RxCombine`partial apply for Subscriber<>.pushRxEvent(_:) at <compiler-generated>:0
    frame #4: 0x0000000103ca83fc RxSwift`closure #1 in ObservableType.subscribe(e=, on=0x0000000103ba05b0 RxCombine`partial apply forwarder for (extension in RxCombine):Combine.Subscriber< where A.Failure == Swift.Error>.pushRxEvent(RxSwift.Event<A.Input>) -> () at <compiler-generated>) at ObservableType+Extensions.swift:23:17
    frame #5: 0x0000000103bd920f RxSwift`AnonymousObserver.onCore(event=, self=0x0000600001b8b570) at AnonymousObserver.swift:22:21
    frame #6: 0x0000000103cb0ea1 RxSwift`ObserverBase.on(event=, self=0x0000600001b8b570) at ObserverBase.swift:16:22
    frame #7: 0x0000000103cb1155 RxSwift`protocol witness for ObserverType.on(_:) in conformance ObserverBase<A> at <compiler-generated>:0
    frame #8: 0x0000000103bec049 RxSwift`BehaviorSubject._synchronized_subscribe<Element>(observer=0x0000600001b8b570, self=0x00006000027a8120) at BehaviorSubject.swift:128:18
    frame #9: 0x0000000103beba1c RxSwift`BehaviorSubject.subscribe<Element>(observer=0x0000600001b8b570, self=0x00006000027a8120) at BehaviorSubject.swift:111:33
    frame #10: 0x0000000103ca833d RxSwift`ObservableType.subscribe(on=0x0000000103ba05b0 RxCombine`partial apply forwarder for (extension in RxCombine):Combine.Subscriber< where A.Failure == Swift.Error>.pushRxEvent(RxSwift.Event<A.Input>) -> () at <compiler-generated>, self=0x00006000027a8120) at ObservableType+Extensions.swift:25:40
    frame #11: 0x0000000103ba0171 RxCombine`RxPublisher.receive<Upstream>(subscriber=<no summary available>, self=0x0000600001590e20) at Observable+Combine.swift:43:58
    frame #12: 0x0000000103ba0455 RxCombine`protocol witness for Publisher.receive<A>(subscriber:) in conformance RxPublisher<A> at <compiler-generated>:0
    frame #13: 0x00007fff233c1571 Combine`Combine.PublisherBox.receive<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(subscriber: A1) -> () + 33
    frame #14: 0x00007fff233c1715 Combine`Combine.AnyPublisher.receive<A where A == A1.Input, B == A1.Failure, A1: Combine.Subscriber>(subscriber: A1) -> () + 53
    frame #15: 0x00007fff2335eb59 Combine`(extension in Combine):Combine.Publisher.subscribe<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(A1) -> () + 873
    frame #16: 0x00007fff233b1023 Combine`Combine.Publishers.ReplaceError.receive<A where A1: Combine.Subscriber, A.Output == A1.Input, A1.Failure == Swift.Never>(subscriber: A1) -> () + 371
    frame #17: 0x00007fff233c1571 Combine`Combine.PublisherBox.receive<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(subscriber: A1) -> () + 33
    frame #18: 0x00007fff233c1715 Combine`Combine.AnyPublisher.receive<A where A == A1.Input, B == A1.Failure, A1: Combine.Subscriber>(subscriber: A1) -> () + 53
    frame #19: 0x00007fff2335eb59 Combine`(extension in Combine):Combine.Publisher.subscribe<A where A1: Combine.Subscriber, A.Failure == A1.Failure, A.Output == A1.Input>(A1) -> () + 873
    frame #20: 0x00007fff2339d539 Combine`(extension in Combine):Combine.Publisher< where A.Failure == Swift.Never>.assign<A>(to: Swift.ReferenceWritableKeyPath<A1, A.Output>, on: A1) -> Combine.AnyCancellable + 265
  * frame #21: 0x00000001035e8a22 SwiftUITest`ContentView.init() at ContentView.swift:62:25
    frame #22: 0x00000001035d9847 SwiftUITest`SceneDelegate.scene(scene=0x00007fa1f0705940, session=0x0000600000083e00, connectionOptions=0x00006000015ad6c0, self=0x00006000015ad700) at SceneDelegate.swift:23:27
    frame #23: 0x00000001035d9d36 SwiftUITest`@objc SceneDelegate.scene(_:willConnectTo:options:) at <compiler-generated>:0
    frame #24: 0x00007fff4761d3e0 UIKitCore`+[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1304
    frame #25: 0x00007fff4808f170 UIKitCore`-[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1018
    frame #26: 0x00007fff4808f4b2 UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 304
    frame #27: 0x00007fff47bfa7f5 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
    frame #28: 0x00007fff365d6165 FrontBoardServices`-[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 442
    frame #29: 0x00007fff365fc4d8 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 + 102
    frame #30: 0x00007fff365e0c45 FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    frame #31: 0x00007fff365fc169 FrontBoardServices`__86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke + 355
    frame #32: 0x000000010400ad48 libdispatch.dylib`_dispatch_client_callout + 8
    frame #33: 0x000000010400dcb9 libdispatch.dylib`_dispatch_block_invoke_direct + 300
    frame #34: 0x00007fff3662237e FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    frame #35: 0x00007fff3662206c FrontBoardServices`-[FBSSerialQueue _queue_performNextIfPossible] + 441
    frame #36: 0x00007fff3662257b FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 22
    frame #37: 0x00007fff23bd4471 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #38: 0x00007fff23bd439c CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #39: 0x00007fff23bd3bcc CoreFoundation`__CFRunLoopDoSources0 + 268
    frame #40: 0x00007fff23bce87f CoreFoundation`__CFRunLoopRun + 1263
    frame #41: 0x00007fff23bce066 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #42: 0x00007fff384c0bb0 GraphicsServices`GSEventRunModal + 65
    frame #43: 0x00007fff48092d4d UIKitCore`UIApplicationMain + 1621
    frame #44: 0x00000001035d8e2b SwiftUITest`main at AppDelegate.swift:15:7
    frame #45: 0x00007fff5227ec25 libdyld.dylib`start + 1
    frame #46: 0x00007fff5227ec25 libdyld.dylib`start + 1
(lldb) 

Stack frame 4 is this code

    public func subscribe(_ on: @escaping (Event<Element>) -> Void)
        -> Disposable {
            let observer = AnonymousObserver { e in
                on(e)   <----- CRASH HERE
            }
            return self.asObservable().subscribe(observer)
    }
freak4pc commented 4 years ago

Please provide a full reproducible project. Thanks !

freak4pc commented 4 years ago

Hey @BrianDoig - any chance for a project with this issue? I couldn't repro it here on Xcode 11.4.1.

freak4pc commented 4 years ago

Closed due to inactivity.