2

I am Swift newcomer from C#. I started designing the Swift program from the abstract level - declaring entities signatures with relationships between them. I caught that protocol in swift is analog of the interface in C# or Java. From my (as Swift newcomer from C#) point of view, generics in protocols implemented in Swift by an uncommon way. I researched about associated types but continuously didn't know how to do the following:

typealias Observer<T> = (T) -> ()

protocol Subscription {
    func cancel()
}

protocol Observable<T> {
    func subscribe(observer: Observer<T>) -> Subscription
}

protocol A {}
protocol B {}
protocol C {}

protocol AbcObservable {
  var aAdded : Observable<A> { get }
  var bAdded : Observable<B> { get }
  var cAdded : Observable<C> { get }
}

The code above is from the imaginary world where generic protocols have the same struct as generic classes. Is it possible to write the code above compilable by associated types using? How? If it is impossible, what is an alternative?


Remark 1

I need described architecture to provide something like the following:

class First: AbcObservable { ... }
class Second: AbcObservable { ... }

class Client {
  let lightDependency: AbcObservable
  init(lightDependency: AbcObservable) {
    self.lightDependency = lightDependency
  }
}

class IoCContainer1 {
  let client: Client
  init() {
    let abc: AbcObservable = First()
    client = Client(abc)
  }
}

class IoCContainer2 {
  let client: Client
  init() {
    let abc: AbcObservable = Second()
    client = Client(abc)
  }
}

class Mock: AbcObservable { ... }

class Test {
  func test() {
    let abcMock = Mock()
    let client = Client(abcMock)
    ...
  }
}

This moment is very painfully for me; I can't understand how I can provide D of SOLID in my code in the case when I need generics.


Remark 2

Observable will not have many implementations in the real situation and it is stupid to do it as abstract. I wanted to write the essence of my problem, and I used Observable in the example to simplify. In a real situation, I had DataManager instead of Observable, that has many implementations.


Remark 3

The question was changed from "How to use protocol with associatedtype in another protocol?" to "What is equivalent for C# or Java generic interface in Swift?"

7
  • Might be useful to watch developer.apple.com/videos/play/wwdc2018/406 Commented Mar 28, 2019 at 0:03
  • 1
    All the answers I want to type here look like "stop stop stop stop stop stop." Do not start with abstract inversion of control. Start with a goal in mind for your software, and from there, extract generic solutions. There are way, way too many protocols here, and they look like they're just trying to stand-in for abstract classes maybe? Protocols are not abstract classes. They're not about inheritance. They're about extension. What actual thing do you want this for? From that, we can design often very elegant solutions (not every time, but often). Commented Mar 28, 2019 at 1:27
  • If you're interested in building observables, take a look at github.com/davedelong/Syzygy/tree/master/SyzygyCore/Properties for one fairly powerful approach (while being much smaller than RxSwift, which is a much more powerful approach). Or you can start with an even simpler model that I use, and is probably much easier to understand: gist.github.com/rnapier/981e86fbf345b049c1df41f63e4a2c6e Commented Mar 28, 2019 at 1:31
  • 1
    "I caught that protocol in swift is analog of the interface in C# or Java." This is probably the heart of your mistake. This isn't a really great analogy. They're not unrelated, but the use of protocols is very different, particularly protocols w/ associated types (what you're thinking of as "generic protocols"). They're definitely not the tool to start with in Swift. They will generally arise out of code where you find duplication, rather than being something you start with. Commented Mar 28, 2019 at 1:34
  • If you're looking for IoC patterns in Swift, see stackoverflow.com/a/55368056/97337 for an example (at the bottom of the answer, not the type-eraser part at the top) Commented Mar 28, 2019 at 1:53

1 Answer 1

2

You've overused protocols here, but this is the syntax you'd want. Several of these pieces can't reasonably be protocols. I have trouble imagining three different implementations of Observable, and certainly not of Subscription. (If you can't imagine three implementations, it's probably not a protocol.) Trying to make Observable into a protocol definitely causes the whole thing to explode.

Subscription is just a wrapper around a function, so that's struct. Observable is naturally a class. The rest is possibly fine; your problems there are mostly syntax.

import Foundation

typealias Observer<T> = (T) -> ()

struct Subscription {
    let cancel: () -> Void
}

final class Observable<T> {
    private var observations: [UUID: Observer<T>] = [:]
    func subscribe(observer: @escaping Observer<T>) -> Subscription {
        let uuid = UUID()
        observations[uuid] = observer
        return Subscription(cancel: { [weak self] in self?.observations[uuid] = nil })
    }
}

protocol A {}
protocol B {}
protocol C {}

protocol AbcObservable {
    var aAdded : Observable<A> { get }
    var bAdded : Observable<B> { get }
    var cAdded : Observable<C> { get }
}

struct First: AbcObservable {
    let aAdded = Observable<A>()
    let bAdded = Observable<B>()
    let cAdded = Observable<C>()
}

struct Second: AbcObservable {
    let aAdded = Observable<A>()
    let bAdded = Observable<B>()
    let cAdded = Observable<C>()
}

struct Client {
    let lightDependency: AbcObservable
    init(lightDependency: AbcObservable) {
        self.lightDependency = lightDependency
    }
}

struct IoCContainer1 {
    let client: Client
    init() {
        let abc: AbcObservable = First()
        client = Client(lightDependency: abc)
    }
}

class IoCContainer2 {
    let client: Client
    init() {
        let abc: AbcObservable = Second()
        client = Client(lightDependency: abc)
    }
}

struct Mock: AbcObservable {
    let aAdded = Observable<A>()
    let bAdded = Observable<B>()
    let cAdded = Observable<C>()
}

class Test {
    func test() {
        let abcMock = Mock()
        let client = Client(lightDependency: abcMock)
    }
}

The Observer implementation here is based on my Subject implementation. Note that this is part of a system that has several quirks that I don't love (Dave Delong's Syzygy is better).

Sign up to request clarification or add additional context in comments.

4 Comments

I understand your answer as "use Observable as concrete type". But my question was how to keep it abstract. I agree with you that Observable will not have many implementations and it is stupid to do it as abstract. I wanted to write the essence of my problem and I used Observable in the example to simplify. In a real situation, I had DataManager instead of Observable, that has many implementations. I added remark in my question to avoid this confusion. Sorry for bad example.
There is no equivalent for C# or Java generic interface in Swift. Protocols are a different thing with different patterns. Can you give an example of the actual DataManager you're trying to implement? Typically the solution is to slice the problem along a different axis than you might in C#.
As an example, take a look specifically at stackoverflow.com/questions/55104199/…. While it is possible to shoe-horn a protocol into that client (ClientType), it is much more powerful and flexible (and easier!) to extract the non-generic piece that actually changes (ClientEngine). I have a similar patterns for key-value stores like UserDefaults if that's the kind of thing you're building.
It's not quite right of me to say there's no equivalent to generic interfaces in Swift. They don't exist today. They will probably exist some day, and we call them Generalized Existentials. I didn't mean to imply that the concept was antithetical to Swift's "protocol oriented programming." GE's will be a very important addition, and we all know it. That said, many cases that cause people to reach for them have better and simpler solutions in Swift, even if they did exist, like the client/transport example above. github.com/apple/swift/blob/master/docs/GenericsManifesto.md

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.