erotsppa
erotsppa

Reputation: 15031

Property must be declared public because it matches a requirement in public protocol?

public protocol CDViewModel: class {
    var cancellableList: [AnyCancellable] { get set }
}
public extension CDViewModel {
   func subscribe(_ callback: @escaping (Project) -> Void) {
    cancellableList.append( /*...*/)
  }
}

I have a protocol that will be used outside of the module. So I have to declare it public.

But I want the class conforming to that to implement cancellableList with private access.

   class MyClass: CDViewModel {
        private var cancellableList: [AnyCancellable] = [AnyCancellable]()
    }

Property 'cancellableList' must be declared public because it matches a requirement in public protocol 'CDViewModel'

Is this possible?

Upvotes: 7

Views: 4410

Answers (2)

Cristik
Cristik

Reputation: 32817

You cannot have some requirements of a protocol being publicly satisfied and hide other ones, you have to have it all or nothing. More, since the protocol extension needs to work with cancellableList, then this property needs to be public on the type that conforms to the protocol.

If you want to hide the cancellableList property, to avoid extrinsic factors messing with it, one approach I can think of is to split the protocol in two, move the cancellableList to the new protocol, and privately declare a property in MyClass.

CDViewModel.swift

public protocol CDViewModel: class {
    // other view model requirements
}

public protocol CDViewModelSource {
    var cancellableList: [AnyCancellable] { get set }
}

public extension CDViewModelSource {
   func subscribe(_ callback: @escaping (Project) -> Void) {
    //cancellableList.append( /*...*/)
  }
}

MyClass.swift

class MyClass {
    private var source = MyClassSource()
    
    func subscribe(_ callback: @escaping (Project) -> Void) {
        source.subscribe(callback)
    }
}

fileprivate class MyClassSource: CDViewModelSource {
    var cancellableList: [AnyCancellable] = []
}

The downside would be that you'd have to talk to the source every time you need to access the list of cancellables.

Upvotes: 0

elight
elight

Reputation: 562

One workaround to this is to create an intermediate object class that can protect access to its variables using fileprivate. You declare that 'wrapper object type' in your MyClass instance thereby conforming to MyClass's required interface.

Now you have full access to the wrapped object (wrappedList) properties from within the protocol extension, but not from outside the module.

CDViewModel.swift

import Combine

class CDViewModelList {
    fileprivate var cancellableList: [AnyCancellable] = [AnyCancellable]()
}

protocol CDViewModelProtocol: AnyObject {
    var wrappedList: CDViewModelList { get }
}

extension CDViewModelProtocol {
   func subscribe(_ callback: Int) {
    self.wrappedList.cancellableList.append(/****/)
  }
}

MyClass.swift

class MyClass: CDViewModelProtocol {
    let wrappedList = CDViewModelList()

    func doStuff () {
        self.subscribe(24)
        self.wrappedList.cancellableList // 'cancellableList' is inaccessible due to 'fileprivate' protection level
    }
}

Thanks for the question it brought me to a good read here:

FULL CREDIT

Upvotes: 1

Related Questions