Some days ago I moved to swift coding after objc. And when I writing adapter with MVVM pattern, I confused with next case, which could be done in objc without any problems.
So in Objc: We have IDTableViewController with presenter (viewModel) property
#interface IDTableViewController : UITableViewController
#property (nonatomic, strong) id<IDCollectionPresenterProtocol> presenter;
#end
And when we inherit IDTableViewController, we override class of presenter in extension (which of course conforms to protocol mentioned above)
UsersViewController.h
#interface UsersViewController : IDTableViewController
UsersViewController.m
#interface UsersViewController ()
#property (nonatomic, strong) UsersPresenter *presenter;
#end
#implementation UsersViewController
#dynamic presenter;
And all is fine... But in swift I can't do the same thing. I can't override class of property in UsersViewController
class IDTableViewController: UITableViewController {
var viewModel : IDCollectionPresenterProtocol?
}
class UsersViewController: IDTableViewController {
var viewModel : UsersPresenter?
}
Can I do this? And How I could implement it, if it's not legal?
There are a couple of approaches:
You can override stored property with computed property that is a front-end to your own private stored property e.g.:
class UsersViewController: IDTableViewController {
private var usersViewModel: UsersPresenter?
override var viewModel: IDCollectionPresenterProtocol? {
get { return usersViewModel }
set { usersViewModel = newValue as! UsersPresenter }
}
}
You could not override viewModel at all, and just implement usersViewModel as a computed property itself if you need a UsersPresenter interface to the view model within UsersViewController:
class UsersViewController: IDTableViewController {
private var usersViewModel: UsersPresenter? {
get { return viewModel as? UsersPresenter }
set { viewModel = newValue }
}
}
The other, more naturally Swifty ways of dealing with this (e.g. generics or protocol with default implementations of the UITableViewDataSource methods in a protocol extension) don't play well with Objective-C, so you'll have problems when using them as view controllers or as a UITableViewDataSource.
Something like this might be what you want:
protocol MyProtocol {
}
class ImplementedProtocol : MyProtocol {
}
class MyBaseClass<T : MyProtocol> {
var viewModel : T?
}
class MyDerivedClass : MyBaseClass<ImplementedProtocol> {
}
Now, when you instantiate MyDerivedClass, viewModel will be of type ImplementedProtocol.
A word of warning: Xcode 7.3 didn't correctly infer the type of viewModel in the autocompletion, but assigning it to a variable fixed that - so it's just an Xcode bug that will hopefully be fixed very soon.
Related
I want to make the existing Objective-C classes conforming to Identifiable protocol so that I could use them as list item in SwiftUI.
#interface MyClass: NSObject // What to do?
#end
For some reason, Googling doesn't help at all. Is it possible?
If not, is there a good alternative that doesn't require me to keep creating wrapper classes?
class MyClassWrapper: Identifiable {
let id = UUID()
var myClass: MyClass
}
You can use an extension to conform to Identifiable in Swift -- then just specify which property should be used as the id:
//obj-c
#interface MyClass : NSObject
#property (strong,nonatomic) NSString* myIdProperty;
#end
//Swift
extension MyClass : Identifiable {
public var id: String {
myIdProperty
}
}
I used NSString/String here, but you could use NSUUID/UUID or any other Hashable-conforming id type.
I don't understand the meaning of this error. This is what I am trying to do:
protocol P {
var testTitle: String? { get }
}
class MyViewController: UIViewController, P {
var testTitle: String? {
didSet {
self.title = testTitle
}
}
}
final class TestViewController: MyViewController {
var testTitle: String? { // Error occurs here
return "Test"
}
}
And I'm getting:
Cannot override mutable property with read-only property 'testTitle'
Could you help me understanding what's going on? Maybe it's trivial for some people but not for me.
Thanks for your help.
You cannot implement a subclass to be more restrictive than the base class (it would only work the other way round). So if the base class implements a read/write property, you cannot make it read only in your derived class.
Therefore:
final class TestViewController: MyViewController {
override var testTitle: String? { // No error occurs here
get {
return "Test"
}
set {
super.testTitle = newValue
}
}
}
The protocol requires that the adopting class implements a property testTitle with a getter but it does not forbid to declare the property with getter and setter.
To override a property you have to override the (entire) signature in the class, not the protocol requirement.
As testTitle is declared as read/write you cannot override it as read-only.
I have a swift protocol, but it never fires.
I have 1 class which is an instance, and the other is a class where I want to manage an object;
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
override init() {
super.init()
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static var instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init()
My issue is that the protocol never fires and does not output "turn is completed"
How can I resolve this?
Edit: How do I set the delegate?
Many thanks
In case you have describe create custom init.
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(with delegate: TurnDelegate?) {
self.delegate = delegate
delegate?.turnIsCompleted()
}
}
Than:
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init(with: c2)
Output:
Turn is completed
You forgot to set the delegate.
Usually the delegate is set in an init method. The method in the protocol is called later in another method for example
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(delegate: TurnDelegate?) {
self.delegate = delegate
}
func turnSomething()
{
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static let instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2 = ClassTwo.instance
let c1 = ClassOne(delegate: c2)
c1.turnSomething()
However for this purpose especially in conjunction with a singleton I'd prefer a callback closure rather than protocol / delegate. The benefit is less overhead and the callback is directly connected to the calling method.
class ClassOne : NSObject {
func turnSomething()
{
let c2 = ClassTwo.instance
c2.turn {
print ("Turn is completed")
}
}
}
class ClassTwo: NSObject {
static let instance = ClassTwo()
func turn(completion: ()->()) {
// do heavy work
completion()
}
}
let c1 = ClassOne()
c1.turnSomething()
Delegates in all their glory do have their drawbacks too. One of them is that relationships between objects and their delegates have to be established explicitly. In Cocoa there are typically two ways of doing this. One is connecting a delegate IBOutlet in InterfaceBuilder, the other is doing it programmatically. As #OlegGordiichuck points out you could do it in the initializer, but generally in Cocoa delegates tend to be properties. In your case this would boil down to instantiate objects of ClassTwo and ClassOne and then manually set the delegate of c2 as in
c2.delegate = c1
This however defeats your notification mechanism and you would have to have a separate method for notifying the delegate (Which is again typical, as usually your delegate cannot know about is significant other during its construction. Moreover the construction of the originator is usually not something the delegate would have to know about).
I have a (swift) class which has a weak pointer to a delegate like this:
import UIKit
#objc public protocol DRSlidingPanelViewControllerDelegate : class {
optional func didSlidePanel(panelHidden : Bool , sender : DRSlidingPanelViewController) -> Void
}
public class DRSlidingPanelViewController: UIViewController {
public weak var delegate : DRSlidingPanelViewControllerDelegate?
///other stuff...
}
Now i make a subclass with another protocol which extends the first, and i want to alter the inherited 'delegate' property
#objc public protocol DRTableViewControllerDelegate : DRSlidingPanelViewControllerDelegate {
optional func someFunction(sender : DRTableViewController) -> Void
}
public class DRTableViewController: DRSlidingPanelViewController {
// public weak var delegate : DRTableViewControllerDelegate?
}
^ this (re)declaration of delegate in the subclass gives me 3 errors when I uncomment it.
Property 'delegate' with type 'DRTableViewControllerDelegate?' (aka 'Optional') cannot override a property with type 'DRSlidingPanelViewControllerDelegate?' (aka 'Optional')
Getter for 'delegate' with Objective-C selector 'delegate' conflicts with getter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Setter for 'delegate' with Objective-C selector 'setDelegate:' conflicts with setter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Now i understand the nature of these errors, and that they are different facets of the one error (attempting to change the 'type' on the delegate pointer.) Can anybody give me a clue how to do this? It obviously can be done, look at how UITableView alters the delegate pointer which it inherits from UIScrollView. In objC I would get a warning which could be silenced with an #dynamic.
Thanks and best regards
edit / addition
Thanks Matt, I do see this previous question, but unfortunately it is closed and I would personally not accept that answer as the definitive answer because it is a compromise.
If I right click on UITableView in xCode and 'jump to definition' I see this
#available(iOS 2.0, *)
public class UITableView : UIScrollView, NSCoding {
public init(frame: CGRect, style: UITableViewStyle) // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
public init?(coder aDecoder: NSCoder)
public var style: UITableViewStyle { get }
weak public var dataSource: UITableViewDataSource?
**weak public var delegate: UITableViewDelegate?**
//plenty other stuff..
}
So I respectfully submit that there definitely is a way to do this.
I would say that as things stand you can't do it. This is disappointing to say the least. You'll just have to call the delegate variable in the subclass something else.
So, this is legal, but of course it totally fails to meet your requirements:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2?
}
If you really hate the multiplication of storage you can make the delegate2 a computed variable that accesses the inherited delegate:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2? {
set {
super.delegate = newValue
}
get {
return super.delegate as? P2
}
}
}
Can someone explain to me why the following playground code gives me the error
EventEntity is not identical to NSManagedObject
import UIKit
import CoreData
class Table<T: NSManagedObject> {
func toFetchedResultsController() -> FetchedResultsController<T> {
return FetchedResultsController<T>()
}
}
class EventEntity: NSManagedObject {
}
class FetchedResultsController<T: NSManagedObject> {
}
class Factory<T: NSManagedObject>: NSObject {
var fetchedResultsController: FetchedResultsController<T>
init(fetchedResultsController: FetchedResultsController<T>) {
self.fetchedResultsController = fetchedResultsController
super.init()
}
}
class TableViewDataSource: NSObject {
init(factory: Factory<NSManagedObject>) {
super.init()
}
}
var dataSource: TableViewDataSource
let fetchedResultsController = Table<EventEntity>().toFetchedResultsController()
let factory = Factory(fetchedResultsController: fetchedResultsController)
dataSource = TableViewDataSource(factory: factory)
I had a problem with an app I'm developing in Swift. I created this playground to reproduce the problem.
What is the reason for this error? EventEntity is actually a subclass of NSManagedObject and T is a generics that specifies a NSManagedObject class requirement.
Thank you!
Look at your TableViewDataSource initializer:
init(factory: Factory<NSManagedObject>) {
super.init()
}
Here, you're specifying that the initializer wants a Factory<NSManagedObject>, and then later you pass it a Factory<EventEntity>. These types are not identical.
I think what you meant to do was to specify that TableViewDataSource also works with a generic type (so long as it's some NSManagedObject), like so:
class TableViewDataSource<T: NSManagedObject>: NSObject {
init(factory: Factory<T>) {
super.init()
}
}