Instantiating a nested class using NSClassFromString in swift - swift

I have a nested class defined like this:
#objc class A {
#objc class B{
}
}
and I need to instantiate A.B using NSClassFromString. I was able to do it for the simple class A but when I attach to the NSClassFromString parameter the .B string it just returns nil.
NSClassFromString("\(appName).A") // works...
NSClassFromString("\(appName).A.B") //doesn't work.
I suppose that since nested class are not available in Objective-c the NSClassFromString just doesn't work for nested classes... in that case is there another way to initialize a nested class from a string?
// EDIT
is curious how the inverse function NSStringFromClassreturns different format when executed for a standard class and a nested class:
"myApp.A" <---- STANDARD CLASS (A)
"_TtCC15myApp13A6B" <----- NESTED CLASS (A.B)
As you can see the format is completely different. What is that _TtCC15? Why the "." has been removed? I suppose that passing the class in that format to NSClassFromString should work.

I find that the following works in a playground (Xcode 8.2 / Swift 3):
// inheriting NSObject is required for `#objc`, at which point `#objc` is optional
class A: NSObject {
class B: NSObject {
override var description: String { return "foo" }
}
}
let str = NSStringFromClass(A.B.self)
guard let anyClass = NSClassFromString(str)
else { fatalError("no class") }
// cast to a type that defines `init()` so we can instantiate
guard let nsClass = anyClass as? NSObject.Type
else { fatalError("class isn't NSObject") }
// call `.init()`, not `nsClass()`; constructor syntax is for static types only
let instance = nsClass.init()
print(instance) // -> "foo"
The oddball class "name" string is because the ObjC runtime doesn't understand nested classes (or other kinds of types that Swift can define but ObjC can't) -- within Swift itself, such mangled names are how types, functions and such get uniquely defined. (For example, name mangling is also how function overloading works: func foo() and func foo(num: Int) -> Bool have different mangled names internally.)
The bridging machinery can still be made to dynamically resolve a class given its properly mangled Swift name, but Swift name mangling is an implementation detail and subject to change. (At least until Swift 4 locks down the ABI.) So it's safe to pass the result of NSStringFromClass to NSClassFromString within the same program, but not safe to (e.g.) record a mangled name once in testing, ship that mangled name as a string literal or resource in your app, and expect it to work with NSClassFromString later.
Instead, if you want your nested class (or any other Swift-defined class) to have a reliably known name for use in the ObjC runtime, give it an #objc(name) (as described in the docs):
#objc class A: NSObject {
#objc(A_B) class B: NSObject { /*...*/ }
}
guard let anyClass = NSClassFromString("A_B")
else { fatalError("no class") }
(Note this snippet won't run by itself -- the class A.B has to be referenced at least once somewhere in your process in order to be registered with the ObjC runtime. This could be as simple as putting let t = A.B.self somewhere in your app's startup code.)

Related

swift subclasses used in generics don't get called when inheriting from NSObject

Partial Solution Update at the end!
Attached is code that produces odd behavior. I copied it out of a swift playground so it should run in one fine.
I created a subclass in my project and passed it to my generic class as the concrete type. However, I quickly noticed that only the base class methods are called. This is shown with myBase and mySub below. Despite the generic class being instantiated as <mySub>, only the base methods are called. The print lines for the subclass are never shown.
Well, I found a simple way around that and that is to not inherit from NSObject. When I used swift native classes, the subclass methods are in fact called. These are secondBase and secondSub.
How do I pass a subclass into a generic class and get the actual subclass to receive calls when inheriting from NSObject?
And why would behavior be different?
import Foundation
// The Protocol
protocol P {
init ()
func doWork() -> String
}
// Generic Class
class G<T: P> {
func doThing() -> String {
let thing = T()
return thing.doWork()
}
}
// NSObject Base Class with Protocol
class A1: NSObject, P {
override required init() {
super.init()
}
func doWork() -> String {
return "A1"
}
}
// NSObject Sub Class
class B1: A1 {
required init() {
super.init()
}
override func doWork() -> String {
return "B1"
}
}
// Swift Base Class
class A2: P {
required init() {
}
func doWork() -> String {
return "A2"
}
}
// Swift Sub Class
class B2: A2 {
required init() {
super.init()
}
override func doWork() -> String {
return "B2"
}
}
print ("Sub class failure with NSObject")
print ("Recieved: " + G<B1>().doThing() + " Expected: B1 - NSObject Sub Class Generic (FAILS)")
print ("\nSub class success with Swift Native")
print ("Recieved: " + G<B2>().doThing() + " Expected: B2 - Swift Sub Class Generic (SUCCEEDS)")
print("")
#if swift(>=5.0)
print("Hello, Swift 5.0")
#elseif swift(>=4.1)
print("Hello, Swift 4.1")
#elseif swift(>=4.0)
print("Hello, Swift 4.0")
#elseif swift(>=3.0)
print("Hello, Swift 3.x")
#else
print("Hello, Swift 2.2")
#endif
Output:
Sub class failure with NSObject
Recieved: A1 Expected: B1 - NSObject Sub Class Generic (FAILS)
Sub class success with Swift Native
Recieved: B2 Expected: B2 - Swift Sub Class Generic (SUCCEEDS)
Hello, Swift 5.0
Partial solution update:
Moving the protocol conformance from the base class to the sub class allows the sub class to behave correctly. Definitions become:
class A1: NSObject
class B1: A1, P
The problem is the base class can no longer be used directly at all when no functionality beyond it is needed. This is mostly an issue if the protocol being conformed to has an associated type. When this is true, you must have a concrete class that conforms to the protocol for use in generics.
One use case here is expecting a base class in the generics (with a protocol involving an associated type) which allows something to function without caring what actual sub class was passed in. This actually ends up being a poor man's form of type erasure in some cases. And you can still use the same generic with the subclass.
G<A1>()
G<B1>()
This was derived from a similar question here: Generic Class does not forward delegate calls to concrete subclass
Partial options are:
remove NSObject and use swift native classes only
when NSObject is required, try to separate protocol conformance from inheritance of NSObject
UPDATE ON THE BELOW IDEA: Doesn't Work
I am going to test if providing an additional layer changes the behavior. Basically have 3 layers, Base class inheriting from NSObject, base Protocol class adding the protocol but inheriting from base and then specific classes. If it can distinguish between the base protocol class and the specific subclass in that case, that would be a functional workaround in all use cases. (and may explain why Apple's NSManagedObject works fine)
Still seems like a bug though.
I was able to confirm your results and submitted it as a bug, https://bugs.swift.org/browse/SR-10617. Turns out this is a known issue! I was informed (by good old Hamish) that I was duplicating https://bugs.swift.org/browse/SR-10285.
In my bug submission, I created a clean compact reduction of your example, suitable for sending to Apple:
protocol P {
init()
func doThing()
}
class Wrapper<T:P> {
func go() {
T().doThing()
}
}
class A : NSObject, P {
required override init() {}
func doThing() {
print("A")
}
}
class B : A {
required override init() {}
override func doThing() {
print("B")
}
}
Wrapper<B>().go()
On Xcode 9.2, we get "B". On Xcode 10.2, we get "A". That alone is enough to warrant a bug report.
In my report I listed three ways to work around the issue, all of which confirm that this is a bug (because none of them should make any difference):
make the generic parameterized type's constraint be A instead of P
or, mark the protocol P as #objc
or, don't have A inherit from NSObject
UPDATE: And it turns out (from Apple's own release notes) there's yet another way:
mark A's init as #nonobjc
This is not an answer so much as a way to avoid the problem.
In most of my code, I did not have to conform to NSObjectProtocol only Equatable and/or Hashable. I have implemented those protocols on the objects that needed it.
I then went through my code, removed all NSObject inheritance except on those classes which inherit from an Apple protocol or object that requires it (Like UITableViewDataSource).
The classes that are required to inherit from NSObject are Generic but they are not typically handed into other Generic classes. Therefore the inheritance works fine. In my MVVM pattern these tend to be the intermediate classes that work with view controllers to make logic like table views reusable. I have a tableController class that conforms to the UITableView protocols and accepts 3 generic viewModel types allowing it to provide the table logic for 95% of my views with no modifications. And when it needs it, subclasses easily provide alternate logic.
This is a better strategy as I am no longer randomly using NSObject for no reason.
This is a second way to avoid the problem.
#matt originally suggested this but then deleted his answer. It is a good way to avoid the problem. His answer was simple. Mark the protocol with objc like this:
// The Protocol
#objc protocol P {
init ()
func doWork() -> String
}
This solves the above sample code and you now get the expected results. But doing this has side effects for swift. At least one of those is here:
How to use #objc protocol with optional and extensions at the same time?
For me, it began a chain of having to make all my protocols objc compatible. That made the change not worth it for my code base. I was also using extensions.
I decided to stay with my original answer at least until Apple fixes this bug or there is a less invasive solution.
I thought this one should be documented in case it helps someone else facing this problem.

How to implement "abstract" property initializers in Swift

So far I have only worked on Objectiv-C projects and now started my first Swift project.
I know that Swift does not support abstract classes but I would like to know what is the best way to model / solve this in Swift:
// Abstract implementation
public abstract class MyClass {
private SomeBaseClass someProperty;
MyClass() {
initProperties();
}
abstract void initProperties(); // Init someProperty with some child class of SomeBaseClass.
}
// Objectiv-C
#implementation MyClass
- (id)init {
self = [super init];
if (self) {
[self initProperties];
}
return self;
}
- (void)initProperties {
// Override in inherited classes
}
// Swift
class MyClass {
// Should not be optional since MyClass should be required to have someProperty != nil
let someProperty: SomeBaseClass;
override init() {
super.init();
initProperties();
}
func initProperties() {
// Cannot be empty since someProperty is non-optional and needs to be initialized
// Cannot be empty since Swift does not support abstract methods
}
}
Of course it would be possible to define someProperty as optional SomeBaseClass? but in this case every time the property is used it has to be tested and unwrapped.
Is there a better way to solve this?
EDIT:
I know that Swift uses protocols to create an abstraction similar to abstract classes. However I do not understand how this concept can solve the concrete problem / question.
In other programming languages the abstract class MyClass can use the property someProperty in many different places while leaving the burden to initialize the property with a value with its concrete subclasses.
Although I read the article linked by #MohamendS and the answers to the possible dublicate answer I do not understand how to achieve the same using protocols.
MyClass has only one abstract function while all other functions are implemented. Thus MyClass itself cannot be a protocol since protocols cannot implement functions (can they?)
MyClass could only implement another protocol which defines that there has to be a initProperties method. But in this case MyClass would need to provide an implementation of this method which brings us back to the same problem.
I guess I can't see the wood for the trees, but how can protocols help here?
Abstraction concept in Swift is used with protocols, i suggest reading this article to know more and here is an example
protocol Abstraction {
var foo: String { get }
func fee()
init(with foo: String)
}
class A: Abstraction {
required init(with foo: String) {
self.foo = foo
}
var foo: String = ""
func fee() {
}
}
Edit: on your point, that
protocols can't implement functions
You can't but what you can do is extends those protocols using extension and give them an initial implementation therefore you don't have to implement them in the class and you can when you feel you'd like to, check the code below
class A: Abstraction {
required init(with foo: String) {
self.foo = foo
}
var foo: String = ""
//you don't have to implement func foo anymore as its extended in the extension
}
extension Abstraction {
func fee() {
print("ok")
}
}
let a = A(with: "foo")
a.fee() // this will trigger the extension implementation,
Now to use init inside the extension body so you wont have to type them in each confirmation, check out the code below
protocol Abstraction {
var foo: String { get set }
func fee()
init()
init(with foo: String)
}
class A: Abstraction {
required init() { }
var foo: String = ""
//you don't have to implement func foo anymore as its extended in the extension
// you don't have to implement the custom init anymore too
}
extension Abstraction {
init(with foo: String) {
self.init()
self.foo = foo
}
func fee() {
print("ok")
}
}
There are many possible answers to this question depending on how MyClass is used. As written, there's of course no reason for someProperty to be in the base class at all, and there's definitely no reason for an initProperties() method (that should be in init).
I realize this is "just an example," but it demonstrates a common problem of creating hierarchies that aren't needed. There are ways to write this code using a semi-abstract base class, but generally that should be avoided, so the first question is what you're using this for and can we avoid this problem entirely?
To answer the question as given, you'd probably start by making a default SomeBaseClass, so that the abstract class can just assign someProperty = SomeBaseClass().
If that's impossible, generally you'd use a ! type:
let someProperty: SomeBaseClass!
And you implement initProperties() with a fatalError:
func initProperties() { fatalError("Implement in subclass") }
Alternately, it can be convenient to implement someProperty as a computed variable, and implement it based on some other property in the subclasses
var someProperty: SomeBaseClass { fatalError() }
But this is really a last resort. Any time you find yourself having to write fatalError you're probably on the wrong track, and you don't need a trick to get around it; you need to reconsider the problem.
You should first think about how MyClass is used, and consider whether it can be a value type. Separately, you should think about whether it can be a protocol that matches the use case. Protocols are not just abstract interfaces that hide implementations. They are a view onto a conforming type to solve a specific problem. That's why there's a Collection protocol that provides access to dozens of algorithms for numerous, otherwise unrelated types, not an ArrayProtocol just to hide the implementation of Array. Don't turn MyClass into MyClassProtocol. Ask what kinds of algorithms want to use types like this one.
When you find yourself creating interlocking hierarchies of types (subclasses of something that require subclasses of some other thing), you have often sliced the problem in the wrong direction. You should rethink whether you could slice the problem so that the varying parts of SomeBaseClass are actually part of MyClass (often this makes SomeBaseClass simpler; for example being pure data rather than having logic).
There's no one right answer here. It depends on the nature of MyClass, so we can't really discuss it in abstract terms. Like abstract classes, solving abstract problems often leads you down the wrong roads. It's often better to start with concrete types and then find their similarities and extract them.
Even with that said, it's worth showing what a simple, naive protocol would look like here. (It's possible this is even the correct protocol.)
public protocol MyProtocol {
var someProperty: SomeBaseClass { get }
}
That's it. That's all you actually express in your current abstract class (and it's not actually clear whether someProperty is public; if it's private, this protocol would be empty).
An implementing struct would then look like:
struct MyStruct: MyProtocol {
var someProperty: SomeBaseClass
}
Or if you wanted a reference type, you could use a final class:
final class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}
Or if you wanted inheritance, you could use a non-final class:
class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}

Swift - how do I pass a type/object (name or instance) into a method and then (a) get the type (b) instantiate it?

I need to do this, but I'm having all sorts of problems:
class MyClass {
}
class SomeClass {
func callMethod(object) {
let theType = object.type // I need the same result as MyClass.self
let newInstance = object() // I need the same result as MyClass()
}
}
let someClass = SomeClass()
let object = MyClass
someClass.callMethod(object)
How do you actually do this?
I want to be able to specify one type (e.g. MyClass). That can be either the type name or an instance of it and then get the two values shown in the code comments.
It would be a lot easier if I passed in two separate variables, but I'm sure I can do it in one.
You can use the .dynamicType property of an instance to get its type. Then you can call a required init method on it (or any other class/static method).
let theType = object.dynamicType
let newInstance = theType.init()
Note: This code compiles, depending on the type of object. If the type has a required init() method, it is safe to call on the dynamicType. However, it also appears that if the type is NSObject the compiler will let you call init() on the dynamicType even though a subclass may or may not have an init() method given how Swift initializer inheritance works. Arguably this is a bug in the compiler, so use this code with caution if you choose to do so. Thanks to #nhgrif for the discussion on the topic.

Store type in variable to use like a type later

I'm trying to store a type in a variable so that I can use it like a 1st class type later on.
class SomeModel {}
let someType = SomeModel.self
let array = Array<someType>()
In this case sure I could have done Array<SomeModel>() instead but I want to generalize it and let subclasses provide the value of someType.
However I get errors like someType isn't a type or use of undeclared type 'someType' on the last line.
If you need to store several type values in array, this works pretty well:
let array: [Any.Type] = [String.self, Int.self]
func someFunc<T>(model: T) -> Array<T> {
let array = Array<T>()
return array
}
let someType = SomeModel.self
let array = someFunc(someType())
Looks like this does what I want. The only drawback is that I have to create an instance of the desired type to pass. In this case its minimal overhead, but it just seems like a waste.
Another thing with using generics like this is it appears that the generic's possible types are computed at compile time, so at run time model.dynamicType doesn't necessarily match T. In most cases it will, but if you are doing any reflection driven stuff make sure to really check your use case well.
These days, this can be more easily and adaptably achieved by using .Type on a class or protocol. Note that only functions available to that root class or protocol will be accessible however, so you should ensure that a required initialiser of some kind is defined. For example:
protocol MyClass {
init(someValue: Int)
}
class MyWrapper {
let myClassType: MyClass.Type
init(classType: MyClass.Type) {
self.myClassType = classType
}
func new(with value: Int) -> MyClassType {
return MyClassType.init(someValue: value)
}
}
You can now initialise this rather silly factory class with a specific class implementing the MyClass protocol and when you call the new() function with an integer value it will generate a new instance of that class and return it.

Can I cast a metaclass object to a protocol type in Swift?

Swift inherited Objective-C's metaclass concept: classes themselves are also considered objects. A class Foo's object's class is Foo.self, and it is of type Foo.Type. If Foo inherits from Bar, then Foo.self can be assigned to a variable of type Bar.Type, too. This has at least two benefits:
it allows to override "static methods";
it's easy to create an instance of an unknown class in a type-safe way and without using reflection.
I'm particularly looking at the second one right now. Just to be sure that everybody understands what I'm after, here's an example:
class BaseFoo {
var description: String { return "BaseFoo" }
}
class DerivedFoo: BaseFoo {
override var description: String { return "DerivedFoo" }
}
let fooTypes: [BaseFoo.Type] = [BaseFoo.self, DerivedFoo.self] // metaclass magic!
for type in fooTypes {
let object: BaseFoo = type() // metaclass magic!
println(object)
}
Now, I have an array of AnyClass objects (any metaclass instance can be assigned to AnyClass, just like any object can be assigned to AnyObject), and I want to find which ones implement a given protocol. The protocol would declare an initializer, and I would instantiate the class just like I do in the example above. For instance:
protocol Foo {
init(foo: String)
}
class Bar: Foo {
required init(foo: String) { println("Bar initialized with \(foo)") }
}
class Baz {
required init() { println("I'm not a Foo!") }
}
let types: [AnyClass] = [Bar.self, Baz.self]
So far so good. Now, the problem is determining if the class implements the protocol. Since metaclass instances are polymorphic, I'd expect to be able to cast them. However, I'm apparently missing something, because Swift won't let me write this:
for type in types {
if let fooType = type as? Foo.Type {
let obj = fooType(foo: "special snowflake string")
}
}
The compiler error I get is:
error: 'Foo' is not identical to 'AnyObject'
Is there any way to determine if a metaclass instance represents a class that implements a protocol, and is there any way to cast that instance into a protocol type?
I tried to declare Foo as a class protocol, but it's apparently not enough.
EDIT: I just tried with the Any type, and while it doesn't cause a syntax error, it crashes the Swift compiler.
As of Xcode 7 beta 2 and Swift 2 it has been fixed. You can now write:
for type in types {
if let fooType = type as? Foo.Type {
// in Swift 2 you have to explicitly call the initializer of metatypes
let obj = fooType.init(foo: "special snowflake string")
}
}
Or if you only want type as type Foo.Type you can use for case
for case let type as Foo.Type in types {
let obj = type.init(foo: "special snowflake string")
}