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.
Related
In swift , super class and protocol is write together like this
Most of times ,this will not make misunderstanding , But because there is a NSObject class and a NSObject protocol with the same name, How to distinguish them?
And how the compiler know to use the NSObject class not the NSObject protocol?
And If I want to use the NSObject protocol not the NSObject class , how to write the class declaration?
The protocol is called NSObjectProtocol not NSObject which is a class. If you want something to conform to NSObjectProtocol exclusively use that:
protocol MyProtocol: NSObjectProtocol { }
On the other hand NSObject conforms to NSObjectProtocol. So in conclusion by inheriting a class from NSObject means it's refering to the NSObject class and (not the NSObject objective-c #protocol) ipso facto conforms it to NSObjectProtocol.
In Swift, NSObject protocol is imported as NSObjectProtocol so there is no naming conflict.
https://developer.apple.com/documentation/objectivec/1418956-nsobject?language=objc
Both a struct and a class conforms to a protocol. I use 2 protocol extensions with where conditions to add an implementation of the var property for both class and struct.
I'm quite surprised to see a compile error for classes only.
Why does this happen for classes and not for structs?
protocol MyProtocol {
var property:String { get }
}
extension MyProtocol where Self == MyStruct {
var property: String { return "" }
}
extension MyProtocol where Self == MyClass {
var property: String { return "" }
}
struct MyStruct : MyProtocol {}
class MyClass : MyProtocol {} //Type 'MyClass' does not conform to protocol 'MyProtocol'
It does not compile because your
extension MyProtocol where Self == MyClass
provides a default method only for MyClass itself, but not for possible subclasses. Changing the constraint to
extension MyProtocol where Self: MyClass
makes the code compile. Alternatively prevent the creation of subclasses with
final class MyClass : MyProtocol {}
This is not a problem for MyStruct because struct types cannot be inherited from in Swift.
Classes can inherit from other classes.
So some class X may inherit from MyClass.
But your extension provides implementation of MyProtocol properties of if the class is MyClass and not it's descendant. So any class that inherits from MyClass won't have any properties of MyProtocol implemented. And that is the problem.
If you declare MyClass final your code would be valid:
final class MyClass : MyProtocol {}
If you extend conditional extension to any MyClass descendants your code would be valid:
extension MyProtocol where Self: MyClass {
var property: String { return "" }
}
But right now all those potential subclasses lack implementation for MyProtocol and it is not allowed.
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.
For instance, I want to extract a Swift protocol from my existing Objective-C class MyFoo. Let's call this protocol FooProtocol.
The situation looks like this:
// In Objective-C
#interface MyFoo
#property(nonatomic, copy) NSString *foo;
#end
#implementation MyFoo
// ... -(instancetype)initWithString: is implemented here
#end
// In Swift
#objc protocol FooProtocol {
var foo: String { get set }
}
extension MyFoo: FooProtocol {
// do nothing here!
}
Then I should be allowed to do this:
let theFoo: FooProtocol = MyFoo(string: "Awesome")
NSLog("\(theFoo.foo)") // Prints awesome.
But I get told "MyFoo does not conform to protocol FooProtocol". Okay. Fair enough, I guess the protocol extension needs a little nudge:
extension MyFoo: FooProtocol {
var foo: String! { get { return foo } set { NSLog("noop") }}
}
but I'm getting errors from the compiler that look like
Getter for 'foo' with Objective-C selector 'foo' conflicts with previous declaration with the same Objective-C selector
What am I doing wrong?
The interfaces have different signatures and therefore the compiler doesn't really know what to do.
Try this:
// In Objective-C
#interface MyFoo
#property(nonatomic, copy) NSString *foo;
#end
#implementation MyFoo
// ... -(instancetype)initWithString: is implemented here
#end
// In Swift
#objc protocol FooProtocol {
var foo: NSString { get set }
}
extension MyFoo: FooProtocol {
// do nothing here!
}
In some tutorials, I've seen:
class MyClass: NSObject {
var a: String!
var b: String!
init(a: String, b: String) {
super.init()
self.a = a
self.b = b
}
}
I was under the impression that swift classes didn't need to subclass anything. Is there any reason why someone would want to subclass from NSObject ?
NSObject class is not so important. What really is important is the NSObject protocol, in Swift named NSObjectProtocol.
For example, most protocols in Cocoa/CocoaTouch inherit from NSObjectProtocol. That means that if you want to use such protocols (for example, to implement delegates), you have to implement all the methods from NSObjectProtocol. That's pretty hard but NSObject class implements them all for you so the easiest solution is to inherit from NSObject class.
When you don't subclass NSObject you lose all the cool stuff Selector that comes with it: performSelector, respondsToSelector etc...
Also - this way you can make sure your Swift code objects can interact with legacy Objective-C code.