I am trying to create a ProfileCellController that I can use to configure a cell in my UITableView.
As my table can have multiple cell types, I was hoping to use generics to set the type on the cell required and use a configure method to set the props.
This is what I have so far:
import UIKit
public protocol ProfileLoadedView {
func display(_ viewModel: ProfileViewModel)
}
public class SingleLineCell: UITableViewCell { }
public class MultiLineWithIconCell: UITableViewCell { }
public final class ProfileCellController<T> where T: UITableViewCell {
private var cell: T?
func view(in tableView: UITableView) -> UITableViewCell {
cell = tableView.dequeueReusableCell()
return cell!
}
}
extension ProfileCellController: ProfileLoadedView where T: SingleLineCell {
public func display(_ viewModel: ProfileViewModel) {
}
}
extension ProfileCellController: ProfileLoadedView where T: MultiLineWithIconCell {
public func display(_ viewModel: ProfileViewModel) {
}
}
extension UITableView {
func dequeueReusableCell<T: UITableViewCell>() -> T {
let identifier = String(describing: T.self)
return dequeueReusableCell(withIdentifier: identifier) as! T
}
}
However the second extension has an error of
Conflicting conformance of 'ProfileCellController' to protocol 'ProfileLoadedView'; there cannot be more than one conformance, even with different conditional bounds
Is it possible to achieve this?
Sadly that's not supported yet. You cannot have several conditional conformances to the same protocol with different conditions.
However, you can make ProfileCellController unconditionally conform to the ProfileLoadedView protocol, provide a default implementation for the display method and then provide more specific implementations for the subclasses.
extension ProfileCellController: ProfileLoadedView {
public func display(_ viewModel: ProfileViewModel) {
print("default")
}
}
extension ProfileCellController where T: SingleLineCell {
public func display(_ viewModel: ProfileViewModel) {
print("singleline")
}
}
extension ProfileCellController where T: MultiLineWithIconCell {
public func display(_ viewModel: ProfileViewModel) {
print("multiline")
}
}
ProfileCellController<SingleLineCell>().display(ProfileViewModel()) // prints singleline
ProfileCellController<MultiLineWithIconCell>().display(ProfileViewModel()) // prints multiline
Why don't you create a class and use some inheritance so your protocol could be just implemented once?
public class GenericCell: UITableViewCell { }
public class SingleLineCell: GenericCell { }
public class MultiLineWithIconCell: GenericCell { }
extension ProfileCellController: ProfileLoadedView where T: GenericCell {
public func display(_ viewModel: ProfileViewModel) {
switch cell {
case is SingleLineCell:
// impl goes here
return
case is MultiLineWithIconCell:
// impl goes here
return
}
}
Related
I´ve got a base ViewController and a base ViewModel. The base ViewModel is used by the base ViewController. Also, I´ve got 2 subclasses of ViewControllers and 2 subclasses of ViewModels that need to be used together.
Example:
class BaseViewModel {
func somethingBasic() {}
}
class ConcreteViewModel1: BaseViewModel {
func somethingConcrete1() {}
}
class ConcreteViewModel2: BaseViewModel {
func somethingConcrete2() {}
}
class BaseViewController {
let viewModel: BaseViewModel
init(with viewModel: BaseViewModel) {
self.viewModel = viewModel
}
}
class ConcreteViewController1: BaseViewController {
init(with viewModel: ConcreteViewModel1) {
super.init(with: viewModel)
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1() //this does not work
}
}
class ConcreteViewController2: BaseViewController {
init(with viewModel: ConcreteViewModel2) {
super.init(with: viewModel)
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2() //this does not work
}
}
The question is: what is the preferred solution to make viewmodel.somethingConcrete1() and viewmodel.somethingConcrete2() work?
Try using Generics for this.
Create init in BaseViewController accepting a generic parameter T constrained to type BaseViewModel, i.e.
class BaseViewController<T: BaseViewModel> {
let viewModel: T
init(with viewModel: T) {
self.viewModel = viewModel
}
}
Now inherit ConcreteViewController1 and ConcreteViewController2 from BaseViewController giving the specific type for generic parameter T, i.e.
class ConcreteViewController1: BaseViewController<ConcreteViewModel1> {
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1()
}
}
class ConcreteViewController2: BaseViewController<ConcreteViewModel2> {
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2()
}
}
I discussed this with a few other colleagues, and we came around with this solution, based on Composition instead of inheritance:
class BaseViewModel {
func somethingBasic() {}
}
class ConcreteViewModel1 {
private let baseViewModel = BaseViewModel()
func somethingConcrete1() {}
func somethingBasic() {
baseViewModel.somethingBasic()
}
}
class ConcreteViewModel2 {
private let baseViewModel = BaseViewModel()
func somethingConcrete2() {}
func somethingBasic() {
baseViewModel.somethingBasic()
}
}
class BaseViewController {}
class ConcreteViewController1 {
private let base = BaseViewController()
private let viewModel: ConcreteViewModel1
init(with viewModel: ConcreteViewModel1) {
self.viewModel = viewModel
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete1()
}
}
class ConcreteViewController2: BaseViewController {
private let base = BaseViewController()
private let viewModel: ConcreteViewModel2
init(with viewModel: ConcreteViewModel2) {
self.viewModel = viewModel
}
func useViewModel() {
viewModel.somethingBasic()
viewModel.somethingConcrete2()
}
}
With that solution, you get the type safety, you avoid Generics and you don´t need to cast anywhere.
I have a situation where I declare two functions in a protocol, one of them takes an optional generically constrained parameter, the other function takes no parameters, but needs to be implemented in an extension as a default function where it calls the one with a parameter and passes nil. However I get this error:
Argument passed to call that takes no arguments
My code:
public protocol MenuAccessible {
var menuEntryViewController: UIViewController { get }
}
public protocol MenuTabBarControllerProtocol {
func reloadTabs<T>(from uiProvider: T?) where T: MenuAccessible
func reloadTabs()
}
public extension MenuTabBarControllerProtocol {
func reloadTabs() {
reloadTabs(from: nil) // error here, can't infer type
}
}
Obviously the compiler is not able to infer the type. If I for example pass a nil (Optional) of the required type, then the compiler is happy. For example:
struct MenuAccessibleObject: MenuAccessible {
var menuEntryViewController: UIViewController { return UIViewController() }
}
public extension MenuTabBarControllerProtocol {
func reloadTabs() {
let menuAccessible: MenuAccessibleObject? = nil
reloadTabs(from: menuAccessible) // passes nil, but compiler is happpy
}
}
Is there a way to pass nil in my default function implementation and not have to create that dummy object?
I don't understand why you are Using Generic T there if you are defining type is MenuAccessible
Following is simply compiler without any issue
public protocol MenuAccessible {
var menuEntryViewController: UIViewController { get }
}
public protocol MenuTabBarControllerProtocol {
func reloadTabs(from uiProvider: MenuAccessible?)
func reloadTabs()
}
public extension MenuTabBarControllerProtocol {
func reloadTabs() {
reloadTabs(from: nil)
}
}
public extension MenuTabBarControllerProtocol {
func reloadTabs(from uiProvider: MenuAccessible?) {
fatalError() // implement me
}
}
EDIT
I don't know this will work for you or not but try this
public protocol MenuAccessible {
var menuEntryViewController: UIViewController { get }
}
public class UIProvider:NSObject {
}
public protocol MenuTabBarControllerProtocol {
func reloadAllTheItems<T>(from uiProvider: T?) where T: UIProvider, T: MenuAccessible
func reloadTabs()
}
public extension MenuTabBarControllerProtocol {
func reloadTabs() {
self.reloadAllTheItems(from: Temp())
}
func reloadAllTheItems (provider:(UIProvider & MenuAccessible)) {
}
}
class Temp: (UIProvider & MenuAccessible) {
var menuEntryViewController: UIViewController {
return UIViewController()
}
}
I'm struggling trying to modelize something that seems basic to me.
Let's consider a fictive service/multicast delegate implementation :
protocol Service { }
protocol Delegate { }
protocol Service1Delegate: Delegate {
func doSomething()
}
protocol Service1: Service {
func foo()
var delegates: [Service1Delegate] { get }
func register(delegate: Service1Delegate)
}
class MyService1: Service1 {
func foo() {
delegates.forEach { $0.doSomething() }
}
private(set) var delegates = [Service1Delegate]()
func register(delegate: Service1Delegate) {
self.delegates.append(delegate)
}
}
class A: Service1Delegate {
func doSomething() {
print("Hello A")
}
}
class B: Service1Delegate {
func doSomething() {
print("Hello B")
}
}
let service1 = MyService1()
service1.register(delegate: A())
service1.register(delegate: B())
service1.foo()
No problem, I get Hello A and Hello B printed.
Now consider a second service
protocol Service2Delegate: Delegate {
func doSomethingElse()
}
protocol Service2: Service {
func bar()
var delegates: [Service2Delegate] { get }
func register(delegate: Service2Delegate)
}
class MyService2: Service2 {
func bar() {
delegates.forEach { $0.doSomethingElse() }
}
private(set) var delegates = [Service2Delegate]()
func register(delegate: Service2Delegate) {
self.delegates.append(delegate)
}
}
Service1 and Service2 both have some code in common relative to delegates that I wish to be made generic.
So I'm introducing a new protocol Delegable like so:
protocol Delegable {
associatedtype D: Delegate
var delegates: [D] { get}
func register(delegate: D)
}
typealias DelegableService = Service & Delegable
and I'd define services like this now
protocol Service1: DelegableService {
func foo()
}
protocol Service2: DelegableService {
func bar()
}
However when implementing them and specifying their delegate type with a typealias, the compiler complains that my implementation do not conform to Delegable
class MyService1: Service1 {
typealias D = Service1Delegate // <- the compiler does not like this, because it's not a concrete type
func foo() {
print("foo")
}
private(set) var delegates = [Service1Delegate]()
func register(delegate: Service1Delegate) {
self.delegates.append(delegate)
}
}
It seems we can only typealias with a concrete type, not a protocol. But the concrete types are unknown for now, and will be of different nature (class A and class B), but they all conform to Service1Delegate.
Is there a solution any way to do that in Swift ?
Not positive I follow your desired outcome, but try this
struct delegateList<T> {
var items: [T]
mutating func add(item: T){
items.append(item)
}
}
protocol Delegable {
var delegates: delegateList<someProtocol>
func register(delegate: someProtocol)
}
First I make a SDK about Bluetooth
ImyBluetoothManager.swift
public protocol ImyBluetoothManager: NSObjectProtocol {
var delegate: myBluetoothDelegate?{get set}
}
public class myBluetooth {
public static func getManager() -> ImyBluetoothManager? {
return myBluetoothManager.sharedInstance()
}
}
myBluetoothManager.swift
class myBluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, myBluetoothManager {
static var instance: myBluetoothManager!
var delegate: myBluetoothDelegate? {
get {
return delegate
}
set {
delegate = newValue
}
}
private override init() {}
static func sharedInstance() ->myBluetoothManager {
if self.instance == nil {
self.instance = myBluetoothManager()
self.instance.setup()
self.instance.scanBLE()
}
return self.instance
}
Then I run it and get myBluetooth.framework,and I new a project 'ExampleApp'
ExampleApp
ViewController.swift
import myBluetooth
class ViewController: UIViewController,myBluetoothDelegate {
override func viewDidLoad() {
let test = SiltaBluetooth.getManager()
test?.delegate = self
super.viewDidLoad()
}
}
Then I run ExampleApp,and warning me.
warning: could not load any Objective-C class information. This will
significantly reduce the quality of type information available.
I have a class with a delegate. I create a subclass, which also has a delegate. I wanted to let the protocol used for the second delegate extend the protocol used for first delegate:
protocol MySuperClassProtocol {
func foo()
}
class MySuperClass {
var delegate:MySuperClassProtocol?
}
protocol MySubClassProtocol:MySuperClassProtocol {
func bar()
}
class MySubClass: MySuperClass {
override var delegate:MySubClassProtocol? // compiler error - "cannot override..."
func test() {
delegate?.foo()
delegate?.bar()
}
}
class UserClass:MySubClassProtocol {
func foo() {
println("foo!")
}
func bar() {
println("bar")
}
}
Is there a way to solve this? The only possible solution I see is to make the 2 protocols independent of each other, and use different names. Like this:
protocol MySuperClassProtocol {
func foo()
}
class MySuperClass {
var mySuperClassDelegate:MySuperClassProtocol?
}
protocol MySubClassProtocol {
func bar()
}
class MySubClass: MySuperClass {
var mySubClassDelegate:MySubClassProtocol?
func test() {
mySuperClassDelegate?.foo()
mySubClassDelegate?.bar()
}
}
class UserClass:MySuperClassProtocol, MySubClassProtocol {
func foo() {
println("foo!")
}
func bar() {
println("bar")
}
}
But this looks a bit weird + will not let me use naming convention for delegate- "delegate".
Sorry for necroposting, the only one solution i found is:
protocol SuperClassDelegate {
func first_method()
}
class SuperClass {
var delegate: SuperClassDelegate?
func do_something() {
delegate?.first_method()
}
}
protocol SubClassDelegate: SuperClassDelegate {
func second_method()
}
class SubClass: SuperClass {
private var subDelegate: SubClassDelegate?
override var delegate: SuperClassDelegate? {
get { return self.subDelegate }
set { self.subDelegate = newValue as! SubClassDelegate? }
}
//override func do_something() {
// super.do_something()
// subDelegate?.second_method()
//}
func do_something_other() {
//subDelegate?.first_method()
self.do_something()
subDelegate?.second_method()
}
}
class InheritanceAndDelegation: SubClassDelegate {
let obj = SubClass()
init() {
obj.delegate = self
}
internal func first_method() {
print("Hello from SuperClass")
}
internal func second_method() {
print("Hello from SubClass")
}
func check() {
obj.do_something_other()
}
}
let inheritanceAndDelegation = InheritanceAndDelegation()
inheritanceAndDelegation.check()
//prints:
//Hello from SuperClass
//Hello from SubClass
Commented code works too. Hope it will be useful for someone.
I was trying to find an ideal solution to this for some time, but could not come up with anything better that this:
protocol BaseDelegateProtocol: class { }
class BaseDelegate: BaseDelegateProtocol { }
class BaseActor {
weak var delegate: BaseDelegate? = nil
}
// MARK: -
protocol ConcreteDelegateProtocol: class {
func doSomething()
}
class ConcreteDelegate: BaseDelegate, ConcreteDelegateProtocol {
func doSomething() {
// Do something
}
}
class ConcreteActor: BaseActor {
private weak var concreteDelegate: ConcreteDelegateProtocol? = nil
override var delegate: BaseDelegate? {
didSet {
concreteDelegate = delegate as? ConcreteDelegateProtocol
}
}
}
Above works in XCode 7 / Swift 2.
This pattern allows adopting more and more protocols on the way down inheriting from BaseDelegate.
There is no need to inherit protocols one from the other, which helps keeping things isolated.
didSet observer on delegate property is automatically called for superclasses, therefore no need to call super.<blah> explicitly, and no risk to 'forget' doing so
Concrete delegate properties can be kept private on each level of inheritance, thereby reducing the clutter.
You can do it in another way, you can add the delegate variable in Subclass and use it to access the SuperClassProtocol also using delegate?.foo().
protocol MySuperClassProtocol {
func foo()
}
class MySuperClass {
//var delegate:MySuperClassProtocol?
}
protocol MySubClassProtocol:MySuperClassProtocol {
func bar()
}
class MySubClass: MySuperClass {
var delegate:MySubClassProtocol?
func test() {
delegate?.foo()
delegate?.bar()
}
}
class UserClass:MySubClassProtocol {
func foo() {
println("foo!")
}
func bar() {
println("bar")
}
}
But the issue with this approach is you can never use MySuperClassProtocol independently unless you create a new SubClass of MySuperClass only for declaring delegate variable.