Objective-C asking for alloc on swift class
Asked Answered
N

5

23

Some small steps to begin wrapping my head around Swift. I've basically ported an old class that simply finds the matching icon for a name and return the appropriate UIImage. The Swift part of things seems to be up and running, and looks (almost) like this:

@objc class ImageHandler{

    func iconForData(data: MyData) -> UIImage{
        let imagesAndNames = [
            "1": "tree.png",
            "2": "car.png",
            "3": "house.png",
            "7": "boat.png",
        ]

        var imageName: String? = imagesAndNames[data.imageName]
        if !imageName{
            imageName = "placeholder.png"
        }
        let icon = UIImage(named: imageName)
        return icon
    }
}

There are no warnings on the above. My old Objective-C class is however asking for an alloc method on the swift class.

ImageHandler *imageHandler = [ImageHandler alloc] init];

Returns the error "No known class method for selector 'alloc' which is true enough I guess, but how do I escape this? Will I have to base my swift-class of NSObject to avoid this?

Nutpick answered 4/6, 2014 at 13:53 Comment(1)
Unrelated but shouldn't it be imagesAndNames[data.imageName]?Saintmihiel
C
49

You declare your ImageHandler class as a root class. It doesn't have alloc method itself. You need to inherit from NSObject:

@objc class ImageHandler : NSObject {

    ...

}

Referenced from this ADF thread.

Chameleon answered 4/6, 2014 at 13:58 Comment(2)
OK, like I expected/feared, small annoyance we'll all have to live with for a long time I guess.Nutpick
Hey, it's the first seed, maybe Apple will change that behavior over the next ones (they even said they do not guarantee the source compatibility).Chameleon
A
25

Just wanted to comment here that if you don't want to subclass NSObject or any other ObjC object, you can declare a class level initializer:

@objc class ImageHandler{

    class func newInstance() -> ImageHandler {
        return ImageHandler()
    }

    func iconForData(data: MyData) -> UIImage{
        let imagesAndNames = [
            "1": "tree.png",
            "2": "car.png",
            "3": "house.png",
            "7": "boat.png",
        ]

        var imageName: String? = imagesAndNames[data.imageName]
        if !imageName{
            imageName = "placeholder.png"
        }
        let icon = UIImage(named: imageName)
        return icon
    }
}

Then in ObjC

ImageHandler * imageHandler = [ImageHandler newInstance];

This way, you don't have to be dependent on ObjC Class inheritance unless you want to.

Airs answered 7/6, 2014 at 1:29 Comment(1)
So the compiler really doesn't give you any class level instantiation by default? I would have thought adding @objc would at least give me something so that I don't have to write my own instantiator every time.Subtropics
E
4

This answer is if you want to keep using pure swift objects, and do not want to inherit from NSObject. If you don't care about using pure swift objects then use akashivskyy's answer above.

I came across this same issue. I took Logan's answer and modified it so that the Objective-C caller does not have to change behavior. This is especially important if you are doing like I am and building a framework that could be consumed by Objective-C or Swift projects.

@objc public class TestClass {

    public init(){}

    public class func alloc() -> TestClass {return TestClass()}

}

The Objective-C implementation gets to stay familiar.

TestClass *testClass = [[TestClass alloc] init];

Swift implementation stays the same as well.

let testClass = TestClass()

Edit: Furthermore I went on to implement this into an class that can be subclassed by other pure swift objects.

@objc public class ObjectiveCCompatibleObject {

    required public init(){}

    public class func alloc() -> Self {return self()}
}
Erickaericksen answered 24/11, 2014 at 18:29 Comment(2)
It's an interesting approach, but does it scale well into situations like TestClass *testClass = [[TestClass alloc] initWithA:a b:b];?Courtroom
Agreed. In the long run I did end up using something similar to Logan's answer for that very reason.Erickaericksen
A
1

If we want to import swift file in Objective C, Should do

import "productname-Swift.h" or use angular brackets <>

in Objective C file. Then can access alloc, init for specific imported swift class in Objective C.

Amply answered 2/9, 2015 at 6:8 Comment(1)
Or import productname-Swift.h in ProductPrefixHeader.pch file, then can access swift classes in any objective classes. Thank You.Amply
S
-1

You could do this:

ImageHandler *imageHandler =
    [[NSClassFromString(@"YourProjectName.ImageHandler") alloc] init];

(or, if you had done @objc(ImageHandler) class ImageHandler in Swift, you would do [[NSClassFromString(@"ImageHandler") alloc] init])


Alternately, you can declare (but not implement) a dummy category containing the alloc method at the top of your Objective-C file:

@interface ImageHandler (Dummy)
+ (instancetype)alloc;
@end

And then you can directly use it in your code:

ImageHandler *imageHandler = [[ImageHandler alloc] init];
Solder answered 25/11, 2014 at 3:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.