Swift override generic swift - swift

I am trying to implement generic for MVVM Swift. I have two base class, an protocol for generic class. The special in here is inheritance. I tried for three hour but I can't fix it :(.
protocol ObjectProtocol {
var id: Int { get set }
var name: String { get set }
}
class BaseViewModel<T: ObjectProtocol> {
var objects: [T] = []
init(){
}
}
protocol ListViewControllerType {
associatedtype T: ObjectProtocol
associatedtype ViewModelType: BaseViewModel<T>
var viewModel: ViewModelType! { get set }
func showError(error: String)
}
extension ListViewControllerType {
func showError(error: String) {
print(error)
}
}
class Consult: ObjectProtocol {
var id: Int = 1
var name: String = "Consult"
}
class ConsultViewModel<T: Consult>: BaseViewModel<Consult> {
}
class ConsultViewController: ListViewControllerType {
var viewModel: ConsultViewModel<Consult>!
}
But I get error in var viewModel: ConsultViewModel<Consult>!
This is error: Type 'ConsultViewController' does not conform to protocol 'ListViewControllerType'
Someone have experience with generic and inheritance can help me please.
Thank you so much.

I usually do like this:
class BaseViewModel {
}
class TemplateViewModel<T: ObjectProtocol>: BaseViewModel {
var objects: [T] = []
init(){
}
}

Related

Swift: Protocol That Accepts Subclasses [duplicate]

This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 3 years ago.
I have a protocol:
protocol InspectableSource
{
var headerView: NSView? { get }
var footerView: NSView? { get }
var mainContentViews: [NSView] { get }
}
Then I have a class that adopts this protocol:
class MyClass: InspectableSource
{
var mainContentViews: [MyNSViewSubclass]
}
The instance of MyClass provides mainContentViews, which is full of objects that are subclasses of NSView. Swift whines about this and won't compile...because it's Swift.
So how do I declare in my protocol that mainContentViews can be ANY type of object that is an NSView or a subclass thereof?
You could use associated types:
protocol InspectableSource
{
associatedtype ViewType : NSView
var headerView: ViewType? { get }
var footerView: ViewType? { get }
var mainContentViews: [ViewType] { get }
}
And in your conforming class:
class MyClass: InspectableSource
{
typealias ViewType = MyNSViewSubclass
var mainContentViews: [MyNSViewSubclass]
// ...
}
But note that this will prevent you from using InspectableSource as the type of a variable.
If you really want to have InspectableSource as the type of a variable. You can try this less type safe approach:
class MyClass: InspectableSource {
Var mainContentViews: [MyNSViewSubclass]
var inspectableMainContentViews: [NSView] {
return mainContentViews
}
}
protocol InspectableSource {
var inspectableMainContentView: [NSView] { get }
}
If you can modify MyClass, I suggest using a dedicated field to store your [MyNSViewSubclass], then conforms to InspectableSource via computed property.
protocol InspectableSource
{
var mainContentViews: [NSView] { get }
}
class MyClass: InspectableSource
{
var mainContentViews : [NSView]{
return myNSViews
}
var myNSViews : [MyNSViewSubclass]
init() {
self.myNSViews = []
}
}

type A requires that type B be a class type swift 4

the following code gives me a compile error
'WeakReference' requires that 'ServiceDelegate' be a class type
protocol ServiceDelegate: AnyObject {
func doIt()
}
class SomeClass() {
// compile error occurs in this line
private var observers = [WeakReference<ServiceDelegate>]()
}
WeakReference code:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
How can I fix this error? Delegate should be declared correctly as per this site.
What I have tried so far:
Changing the delegate protocol conformance from AnyObject to
class does not solve the problem.
Try the above code in a clean playground.
You can't have a WeakReference<ServiceDelegate>. ServiceDelegate itself is not an AnyObject, it just requires that anything that conforms to it be an AnyObject.
You would need to make SomeClass generic and use the generic type as the type for the WeakReference:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
If the generic on SomeClass is too constricting and you want to be able to have instances of multiple unrelated classes as observers then I would do it by abandoning the generic parameter on WeakReference:
final class WeakServiceDelegate {
private(set) weak var value: ServiceDelegate?
init(value: ServiceDelegate?) {
self.value = value
}
}
class SomeClass {
private var observers = [WeakServiceDelegate]()
}
Alternatively you could make WeakReference conditionally conform to ServiceDelegate:
extension WeakReference: ServiceDelegate where T: ServiceDelegate {
func doIt() {
value?.doIt()
}
}
And then use an array of ServiceDelegate in SomeClass:
class SomeClass {
private var observers = [ServiceDelegate]()
func addObserver<T: ServiceDelegate>(_ observer: T) {
observers.append(WeakReference(value: observer))
}
}
As you see, ServiceDelegate is a protocol, not a class type.
Even if all types which can conform to ServiceDelegate are class types, ServiceDelegate itself is not a class type. It is the fact of the pure Swift protocols currently.
Try #obc, Objective-C protocols are a bit different:
#objc protocol ServiceDelegate {
func doIt()
}
You may want to exclude Objective-C something and to make some pure Swift classes conform to ServiceDelegate, but I cannot find other ways around.
The problem is that WeakReference<ServiceDelegate> is wrong at line
private var observers = [WeakReference<ServiceDelegate>]()
You have to use a concrete class instead of protocol inside <>
You have two possible solutions:
Create a concrete class and use it:
class ServiceClass: ServiceDelegate {
//...
}
private var observers = [WeakReference<ServiceClass>]()
Or use a protocol. I mean this:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
protocol SomeContainer: AnyObject { }
extension WeakReference: SomeContainer { }
and use this way:
private var observers = [SomeContainer]()
Note
Using this way:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
You just move the problem to another part of the code.
I had similar problem and ended up keeping generic WeakReference, but removing type constraint:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map { $0 as AnyObject }
}
}
init(value: T?) {
self.value = value
}
}
This works for classes, Objective-C protocols and Swift protocols:
protocol P: class {}
#objc protocol Q {}
class Z: P, Q {}
var z = Z()
var rz = WeakReference<Z>(value: z)
var rp = WeakReference<P>(value: z)
var rq = WeakReference<Q>(value: z)
assert(rz.value === z)
assert(rp.value === z)
assert(rq.value === z)
z = Z()
assert(rz.value === nil)
assert(rp.value === nil)
assert(rq.value === nil)
Unfortunately it compiles for other things too:
protocol R {}
struct S: R {}
var rr = WeakReference<R>(value: S())
print("rr =", rr.value as Any) // nil
var rs = WeakReference<S>(value: S())
print("rs =", rs.value as Any) // nil
In Swift anything can be casted to AnyObject, but for value types that means boxing - new instance is allocated and immediately lost, so it always produces nil.
This can be used to implement an assertion that casting to AnyObject preserves identity:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map {
let asObject = $0 as AnyObject
assert(asObject === $0 as AnyObject)
return asObject
}
}
}
init(value: T?) {
self.value = value
}
}
Alternative approach would be to use https://github.com/wickwirew/Runtime to validate kind of T.self.
create plain protocol
public protocol AnyWeakValue {
var anyValue: Any? { get }
}
inherit associatedtype protocol from AnyWeakValue
public protocol WeakValue: AnyWeakValue {
associatedtype ValueType
var value: ValueType? { get }
}
extension WeakValue {
public var anyValue: Any? { return value }
}
create class Weak inherit WeakValue
open class Weak<Value: AnyObject>: WeakValue {
public init(value: Value?) { self.value = value }
open private(set) weak var value: Value?
}
using example
private var _delegates: [AnyWeakValue] = []
public var delegates: [SomeProtocol] {
return _delegates.compactMap({$0.anyValue as? SomeProtocol})
}
public func register<Delegate>(_ delegate: Delegate) where Delegate: SomeProtocol {
let weak: Weak<Delegate> = Weak.init(value: delegate)
_delegates.append(weak)
}

How to present PublishSubject as Observable in MVVM?

I have something like this:
protocol ViewModel: class {
var eventWithInitialValue: Observable<Int> { get }
}
class ViewModelImpl: ViewModel {
let eventWithInitialValue: BehaviorSubject<Int> = BehaviorSubject(value: 0)
init() {
eventWithInitialValue.onNext(1)
}
}
class ViewController: UIViewController {
weak var viewModel: ViewModel?
private let bag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel?
.eventWithInitialValue
.subscribe(onNext: {
print($0)
}).disposed(by: bag)
}
}
I want to communicate with viewModel fields from ViewController as an Observables. But inside viewModel this fields should be a [OneOf]Subject types (for safety reasons).
Implementation above have a next compile time error -> Type 'ViewModelImpl' does not conform to protocol 'ViewModel'
Can anyone help with the implementation of these requirements?
Your problem is not with Rx, your error is related to your protocol
This will solve the current issue
protocol ViewModel: class {
var eventWithInitialValue: BehaviorSubject<Int> { get }
}
class ViewModelImpl: ViewModel {
var eventWithInitialValue: BehaviorSubject<Int> = BehaviorSubject(value: 0)
init() {
eventWithInitialValue.onNext(1)
}
}
I think you got this Type 'ViewModelImpl' does not conform to protocol 'ViewModel' because you define the eventWithInitialValue's type in your implementation as BehaviorSubject.
What I can suggest is something like this
protocol ViewModel {
var data: Observable<Int> { get}
}
class ViewModelImpl: ViewModel {
private let dataSubject = BehaviorSubject(value: 1)
var data: Observable<Int> {
return dataSubject
}
}

Swift protocol requring method returning protocols

I am trying to make following code compile.
Basically I want to have object conforming multiple protocols, for example network service that exposes different groups of API to different parts of app (for example: login API to loginController, sharing API to shareController)
+
I want this service to be injected using networkXProvider protocols e.g. implement Dependency Injection (for example I want to pass to LoginController an Injection that adopts LoginControllerProvider and FetchingUsersFromDBProvider):
protocol MyAccountCommunicator {
func getAccountData() -> String
}
protocol EventsCommunicator {
func getEvents() -> String
}
class NetworkManager: MyAccountCommunicator, EventsCommunicator {
func getAccountData() -> String {
return "Accounts"
}
func getEvents() -> String {
return "Events"
}
}
protocol MyAccountCommunicatorProvider {
var networkCommunicator: MyAccountCommunicator { get }
}
protocol EventsCommunicatorProvider {
var networkCommunicator: EventsCommunicator { get }
}
class Injector: MyAccountCommunicatorProvider, EventsCommunicatorProvider {
var networkCommunicator: NetworkManager = NetworkManager()
}
Playground fails with following error:
error: strings.playground:29:7: error: type 'Injector' does not
conform to protocol 'MyAccountCommunicatorProvider' class Injector:
MyAccountCommunicatorProvider, EventsCommunicatorProvider {
^
strings.playground:30:9: note: candidate has non-matching type
'NetworkManager'
var networkCommunicator: NetworkManager = NetworkManager()
^
error: strings.playground:29:7: error: type 'Injector' does not
conform to protocol 'EventsCommunicatorProvider' class Injector:
MyAccountCommunicatorProvider, EventsCommunicatorProvider {
^
strings.playground:30:9: note: candidate has non-matching type
'NetworkManager'
var networkCommunicator: NetworkManager = NetworkManager()
Environment: swift4, XCode9
Why do you need MyAccountCommunicatorProvider and EventsCommunicatorProvider protocols? What if you combine them into one?
protocol CommunicatorProvider {
var networkCommunicator: NetworkManager { get }
}
class Injector: CommunicatorProvider {
var networkCommunicator = NetworkManager()
}
protocol MyAccountCommunicator {
func getAccountData() -> String
}
protocol EventsCommunicator {
func getEvents() -> String
}
protocol MyAccountCommunicatorProvider {
var accountCommunicator: MyAccountCommunicator { get }
}
protocol EventsCommunicatorProvider {
var eventCommunicator: EventsCommunicator { get }
}
class NetworkManager: MyAccountCommunicator, EventsCommunicator {
func getAccountData() -> String {
return "Accounts"
}
func getEvents() -> String {
return "Events"
}
}
class Injector: MyAccountCommunicatorProvider, EventsCommunicatorProvider {
var networkManager = NetworkManager()
var accountCommunicator: MyAccountCommunicator {
return networkManager
}
var eventCommunicator: EventsCommunicator {
return networkManager
}
}
You can use like this :
protocol MyAccountCommunicatorProvider {
var networkCommunicator1: MyAccountCommunicator { get }
}
protocol EventsCommunicatorProvider {
var networkCommunicator2: EventsCommunicator { get }
}
class Injector: MyAccountCommunicatorProvider, EventsCommunicatorProvider {
var networkCommunicator: NetworkManager = NetworkManager()
lazy var networkCommunicator1: MyAccountCommunicator = {
return networkCommunicator
}()
lazy var networkCommunicator2: EventsCommunicator = {
return networkCommunicator
}()
}

How to apply Inheritance to Generics in Swift

I have a generic Container struct that is stored as a container of a class that implements a protocol as below:
protocol Protocol {}
class Class : Protocol {}
struct Container<Element> {
var value:Element
init(_ v:Element) { value = v }
}
class Example {
var container : Container<Class>
init() { container = Container(Class()) }
func getContainer() -> Container<Protocol> { return container }
}
However when I pass it I want to pass it as a Container of just the protocol which is generating an error on return container:
Cannot convert return expression of type 'Container<Class>' to return type 'Container<Protocol>'
I know this should be possible as Swift does it with arrays as below:
class ArrayExample {
var arrayContainer : [Class]
init() { arrayContainer = [Class()] }
func getArrayContainer() -> [Protocol] { return arrayContainer }
}
So does anyone have any thoughts on how to implement Container so that the conversion/copy doesn't create the error
It isn't clear what you're actually trying to accomplish, but here's code that compiles and that covers all the goals you've expressed so far:
protocol Protocol {}
class Class : Protocol {
var thing : AnyObject?
}
class Container<Element> {
var value:Element
init(_ v:Element) { value = v }
}
class Example {
var container : Container<Protocol>
init() { container = Container(Class()) }
func getContainer() -> Container<Protocol> { return container }
func doStuff() {
if let v = container.value as? Class { v.thing = "ha" }
}
}