implementing objective C protocol with initializer in swift - objective-c

I have this objective-C protocol
#protocol Parcelable <NSObject>
#required
- (instancetype)initWithData:(NSDictionary *)data;
#end
That I can't seem to find a way to implement in a Swift class. I have tried
required init(data: NSDictionary) {
super.init()
... //do something with data
}
and even
func initWithData(data: NSDictionary) -> Self {
... //do something with data
}
But it keeps saying that the class does not conform to protocol 'Parcelable'
Any ideas?

Aha! the correct way to implement this protocol is
required init(data: [NSObject : AnyObject]) {
super.init()
... //do something with data
}
Swift translates NSDictionary to [NSObject : AnyObject] and does not recognize NSDictionary itself (?)

Related

Swift protocol called in Objective-C not working and application crashes with error message "unrecognized selector"

I am trying to get the Swift protocol to work in Objective-C file, but the application crashes when the error as below.
+[OpenCamera onCameraClose]: unrecognized selector sent to class 0x102ff8580
I am not sure as to what I am missing.
//Swift: UIViewController Code
#objc protocol CameraViewControllerDelegate {
func onCameraClose()
}
#objc class CameraViewController: UIViewController {
var delegate : CameraViewControllerDelegate? = nil
func closeCamera(sender: Any) {
delegate?.onCameraClose()
}
}
// Objective-C : UIViewController Code
OpenCamera.h
#interface OpenCamera : UIViewController <CameraViewControllerDelegate>
OpenCamera.m
#import <MyProjectName/MyProjectName-Swift.h>
#implementation OpenCamera
- (void)viewDidLoad {
[super viewDidLoad];
CameraViewController *cameraViewController = [[CameraViewController alloc] initWithNibName:#"CameraView" bundle:nil];
cameraViewController.delegate = self; //Warning - Incompatible pointer types assigning to 'id<CameraViewControllerDelegate> _Nullable' from 'Class'
}
- (void)onCameraClose {
NSLog(#"Swift Protocol method executed from Objective-C");
}
#end
The warning here did in fact predict the crash:
cameraViewController.delegate = self;
//Warning - Incompatible pointer types assigning to 'id<CameraViewControllerDelegate> _Nullable' from 'Class'
Clearly it thinks self is a class, not an instance. That's very odd.
My guess is that there is something wrong with your import arrangements, but you have not shown enough information to see what it is. I'll just show an arrangement that works.
Let's assume you have both Objective-C and Swift code in one target (i.e. that no frameworks are involved). Then in Swift, you say:
#objc protocol CameraViewControllerDelegate {
func onCameraClose()
}
class CameraViewController: UIViewController {
#objc var delegate : CameraViewControllerDelegate? = nil
func closeCamera(sender: Any) {
delegate?.onCameraClose()
}
}
Note the use of #objc var to expose the delegate property. There is no need to expose the class to Objective-C, as it is already an NSObject derivative.
Okay, in Objective-C, here is your interface file:
#import <UIKit/UIKit.h>
#interface OpenCamera : UIViewController
#end
Note that you do not import the generated header in a .h file, and you do not attempt to mention an imported protocol here.
On to the implementation file:
#import "OpenCamera.h"
#import "MyProject-Swift.h"
#interface OpenCamera () <CameraViewControllerDelegate>
#end
#implementation OpenCamera
- (void)viewDidLoad {
[super viewDidLoad];
CameraViewController *cameraViewController = [[CameraViewController alloc] initWithNibName:#"CameraView" bundle:nil];
cameraViewController.delegate = self;
}
- (void)onCameraClose {
NSLog(#"Swift Protocol method executed from Objective-C");
}
#end
We import the corresponding .h file and the generated header .h file here. We use an anonymous category to declare conformance to the protocol, and the rest is as you have it. You won't see any warnings.

Can not find Swift Protocol declaration in Obj-C class

I have created on Class in Swift and that class and its protocol I am using in Obj-C enabled project but I am getting below error while compiling my project.
cannot find protocol declaration for 'SpeechRecognizerDelegate'; did
you mean 'SFSpeechRecognizerDelegate'?
Can anyone guide me on this how can I use Swift class protocol in my Obj-C class.
Here is my Swift code:
protocol SpeechRecognizerDelegate : class {
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
class SpeechRecognizer: NSObject, SFSpeechRecognizerDelegate {
open weak var delegate: SpeechRecognizerDelegate?
}
Protocol use in Obj-C:
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Let me know if required more info.
Thanks in Advance.
in Swift:
#objc public protocol YOURSwiftDelegate {
func viewReceiptPhoto()
func amountPicked(selected: Int)
}
class YourClass: NSObject {
weak var delegat: YOURSwiftDelegate?
}
in Objective-C headerFile.h
#protocol YOURSwiftDelegate;
#interface YOURController : UIViewController < YOURSwiftDelegate >
in Objective-C Implementation.m
SwiftObject * swiftObject = [SwiftObject alloc] init];
swiftObject.delegate = self
Define your Swift protocol like this inside your Swift file.
#objc protocol SpeechRecognizerDelegate: class{
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
Create a Swift Module inside your project setting then use it. You can find here complete blog for the mix language coding.
Then use Protocol inside Objective C class,
We required to add protocol inside Objective C file -
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Then you need to conform to the protocol methods -
- (void)viewDidLoad {
[super viewDidLoad];
SpeechRecognizer * speechRecognizer = [[SpeechRecognizer alloc] init];
speechRecognizer.delegate = self;
}
#pragma mark - Delegate Methods
-(void)speechRecognitionFinished:(NSString *) transcription{
//Do something here
}
-(void)speechRecognitionError:(NSError *) error{
//Do something here
}
I had a similar issue after following (import header + Objc annotation on protocol). I got the warning when using Swift code from Objective C headers. Solved by only importing into the implementation .m files.
Add #objc attribute to your protocol:
#objc protocol SpeechRecognizerDelegate : class {
//...
}
Include Swift Classes in Objective-C Headers Using Forward Declarations
//MySwiftClass.swift
#objc protocol MySwiftProtocol {}
#objcMembers class MySwiftClass {}
// MyObjcClass.h
#class MySwiftClass;
#protocol MySwiftProtocol;
#interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
#end

How to use NSSet<Class> in Swift (exported as Set<NSObject>)?

I have to fulfill a protocol requirement that is defined in Objective-C like this:
#protocol AProtocol <NSObject>
+ (NSSet<Class> * _Nullable)someClasses;
#end
I want to implement this protocol in a subclass written in Swift. I want to return a Set of classes of another Object. The class I want to return is defined like this:
class B: NSObject {}
The class that conforms to the protocol is defined like this:
class A: NSObject, AProtocol {
static func someClasses() -> Set<NSObject>? {
return [B.self]
}
}
Why is NSSet<Class> bridged to Set<NSObject> instead of Set?
This solution is crashing, how can I solve the problem?
NSSet<Class> is bridged to Set<NSObject> because AnyClass does not conform to Hashable which is a precondition for the ValueType of Set.
It can be solved with the following extension for NSObjectProtocol:
extension NSObjectProtocol where Self: NSObject {
static var objcClass: NSObject {
return (self as AnyObject) as! NSObject
}
}
This returns the class of the object casted as NSObject. It is necessary to cast it first to AnyObject because the type system of Swift is so strong that it would not compile or give a warning when directly casting a type to an instance type. In Objective-C this is fine because Class is also just an object. Since NSObject is implemented in Objective-C and the extension is just for NSObjectProtocol, this is save to use (even with the force cast).
Implementing the extension on NSObjectProtocol and not on NSObject itself brings the positive effect that it is not exported to Objective-C.

Swift 1.2 error on Objective-C protocol using getter

This Objective-C protocol used to work in Swift 1.1, but now errors in Swift 1.2.
Objective-C Protocol stripped down to the one problematic property:
#protocol ISomePlugin <NSObject>
#property (readonly, getter=getObjects) NSArray * objects;
#end
class SomePlugin: NSObject, ISomePlugin {
var objects: [AnyObject]! = nil
func getObjects() -> [AnyObject]! {
objects = ["Hello", "World"];
return objects;
}
}
Here is the Swift 1.2 error:
Plugin.swift:4:1: error: type 'SomePlugin' does not conform to protocol 'ISomePlugin'
class SomePlugin: NSObject, ISomePlugin {
^
__ObjC.ISomePlugin:2:13: note: protocol requires property 'objects' with type '[AnyObject]!'
#objc var objects: [AnyObject]! { get }
^
Plugin.swift:6:9: note: Objective-C method 'objects' provided by getter for 'objects' does not match the requirement's selector ('getObjects')
var objects: [AnyObject]! = nil
^
If I change the Protocol (which I cannot really do since it is from a third party) to the following, the code compiles fine.
// Plugin workaround
#protocol ISomePlugin <NSObject>
#property (readonly) NSArray * objects;
- (NSArray *) getObjects;
#end
Thanks in advance.
You can implement
#property (readonly, getter=getObjects) NSArray * objects;
as a read-only computed property with an #objc attribute
specifying the Objective-C name.
If you need a stored property as backing store
then you'll have to define that separately.
Example:
class SomePlugin: NSObject, ISomePlugin {
private var _objects: [AnyObject]! = nil
var objects: [AnyObject]! {
#objc(getObjects) get {
_objects = ["Hello", "World"];
return _objects;
}
}
}

Method signature for a Selector

I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-)
At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.
Given the following method signature
-(void)execute:(SEL)callback;
is there a way to enforce the signature for the selector passed to such a method?
The method is expecting a selector of a method with the following signature
-(void)foo:(NSData*)data;
But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the
execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.
The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.
One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.
In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:
FooController.h
#protocol FooControllerDelegate
#required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#interface FooController : NSObject
{
id <FooControllerDelegate> * delegate;
}
#property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
#end
FooController.m
#interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
#end
#implementation FooController
#synthesize delegate;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
delegate = nil;
...
return self;
}
- (void)doStuff
{
...
[self handleData:data];
}
- (void)handleData:(NSData *)data
{
if (delegate != nil)
{
[delegate handleData:data forFoo:self];
}
else
{
return;
// or throw an error
// or handle it yourself
}
}
#end
Using the #required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the #required protocol method will result in a compiler error.
Here is how you would create a delegate class to work with the above code:
#interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
// do something here
}
#end
And here is how you would use everything:
FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
// did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.
You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:
- (void)execute:(id)object
{
// Do the execute stuff, then...
if ([object respondsToSelector:#selector(notifyOnExecute:)]) {
[object notifyOnExecute:self];
}
// You could handle the "else" case here, if desired
}
If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.