Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

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()}
}

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.

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];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top