3

I'm having a problem when overriding a function from the ReSwift Pod. I've got the following mock class:

import Foundation
import Quick
import Nimble
import RxSwift
@testable import MainProject
@testable import ReSwift

    class MockReSwiftStore: ReSwift.Store<MainState> {
    var dispatchDidRun: Bool = false
    var subscribeWasTriggered: Bool = false

    init() {
        let reducer: Reducer<MainState> = {_, _  in MainState() }
        super.init(reducer: reducer, state: nil)
    }

    required init(
        reducer: @escaping (Action, State?) -> State,
        state: State?,
        middleware: [(@escaping DispatchFunction, @escaping () -> State?) -> (@escaping DispatchFunction) -> DispatchFunction]) {
        super.init(reducer: reducer, state: state, middleware: middleware)
    }

    override func subscribe<SelectedState, S>(
        _ subscriber: S,
        transform: ((Subscription<MainState>) -> Subscription<SelectedState>)?)
        where S: StoreSubscriber,

        S.StoreSubscriberStateType == SelectedState {
            subscribeWasTriggered = true
        }
    }
}

And when overriding the subscribe method I'm getting following errors

enter image description here

Then when using autocomplete it also shows 2 occurences: enter image description here

However when looking for the original function there's only one which looks like this

open func subscribe<SelectedState, S: StoreSubscriber>(
    _ subscriber: S, transform: ((Subscription<State>) -> Subscription<SelectedState>)?
) where S.StoreSubscriberStateType == SelectedState
{
    // Create a subscription for the new subscriber.
    let originalSubscription = Subscription<State>()
    // Call the optional transformation closure. This allows callers to modify
    // the subscription, e.g. in order to subselect parts of the store's state.
    let transformedSubscription = transform?(originalSubscription)

    _subscribe(subscriber, originalSubscription: originalSubscription,
               transformedSubscription: transformedSubscription)
}

This is my compiler output enter image description here

I'm out of ideas so any help is greatly appreciated Thanks!

Steven B.
  • 1,429
  • 2
  • 19
  • 38

1 Answers1

2

Here is your issue:

class Some<T> {

    func echo() {
        print("A")
    }

}

extension Some where T: Equatable {

    func echo() {
        print("B")
    }

}


class AnotherSome: Some<String> {

    override func echo() {
        print("Doesn't compile")
    }

}

The problem is: ReSwift developers declare Store.subscribe behavior as a part of interface and as a part of extension (I am not sure why they chose to do it instead of introducing other objects). Swift can't figure out which part you are trying to override and thus it doesn't compile. Afaik there are no language instruments which allow you to resolve this issue.

A possible solution is to implement MockStore as a StoreType and use Store object to implement behavior for StoreType interface.

Timofey Solonin
  • 1,393
  • 13
  • 20
  • the issue is, you can't use `StoreType` as a specific type because of associatedtype inside – Vladyslav Zavalykhatko Jan 26 '18 at 13:53
  • 1
    @VladHatko type erase it – Timofey Solonin Jan 26 '18 at 13:58
  • yes, but for testing - I don't really see how you plan to use it. to substitude `Store` by `MockStore` you have to use var with type of `StoreType`, which you can't do because of the `associatedtype`. does that make sense? – Vladyslav Zavalykhatko Jan 26 '18 at 14:01
  • @VladHatko that's the point of type erasure. You will have to subtype the `StoreType` with `MyStoreType` which will add a `func toAnyStore() -> AnyStoreType` and create a class `AnyStoreType` which takes `origin` and tunnels `StoreType` methods and use it as a dependency throughout the app. – Timofey Solonin Jan 26 '18 at 14:22
  • @VladHatko that's how `RxSwift` does it for example – Timofey Solonin Jan 26 '18 at 14:23
  • still don't understand you. what is `AnyStoreType` in your code? – Vladyslav Zavalykhatko Jan 26 '18 at 14:31
  • @VladHatko `AnyStoreType: MyStoreType` and `protocol MyStoreType: StoreType { func toAnyStore() -> AnyStoreType }`. Now for every concrete `MyStoreType` you create, you also have to provide such type erasure, and the users of your concrete implementation will only accept `AnyStoreType` as a dependency. – Timofey Solonin Jan 26 '18 at 14:53
  • Here is a simple example on how type erasure can be used and implemented: https://www.natashatherobot.com/swift-type-erasure/ – Timofey Solonin Jan 26 '18 at 14:53
  • But I would highly suggest you to study how interaction between `Observable`, `ObservableType` and different concrete implementations such as `PublishSubject` is implemented in `RxSwift`. It will provide you with a valuable insight on how to work around the absence of generic protocols in Swift. – Timofey Solonin Jan 26 '18 at 14:55