iOS11 Swift 4 - how to check if Swift class conforms to protocol defined in Objective-C? - objective-c

I have a legacy code base with code written in Objective-C. I'm adding a new class written in Swift which has to conform to existing protocols defined in Objective-C.
How can I make sure my Swift class correctly implements methods defined in Objective-C protocol?
//In Obj-C
#protocol OBJCLocationObserver <NSObject>
- (void)didUpdateLocationWithModel:(nullable Model *)locationModel
lastLocation:(CLLocationCoordinate2D)lastLocation;
#end
//In Swift
extension SwiftLocationManager : OBJCLocationObserver
{
public func didUpdateLocation(with model: Model?, lastLocation: CLLocationCoordinate2D) {
// How to verify this function signature is actually conforming to the Obj-C protocol and is not a new method?
}
}

[MyClass conformsToProtocol:#protocol(MyProtocol)];
According to Apple Docs you can use conformsToProtocol:which returns a Boolean value that indicates whether the receiver conforms to a given protocol.
Example
#protocol MyProtocol
- (void)helloWorld;
#end
#interface MyClass : NSObject <MyProtocol>
#end
Will be exposed as:
console.log(MyClass.conformsToProtocol(MyProtocol));
var instance = MyClass.alloc().init();
console.log(instance.conformsToProtocol(MyProtocol))

Make sure you #import your protocol definition file into the <ProjectName>-Bridging-Header.h file:
#import "OBJCLocationObserver.h"
And then you should see error messages if your signature does not match.
You can also use Xcode Auto Completion. Type:
public func didUpdateLocation
and Auto Complete suggests:
public func didUpdateLocation(withModel Model?, lastLocation: CLLocationCoordinate2D)
which is different than what you have and explains why it isn't working.
Here is another way to get the interface:
As #MartinR suggested on a comment to another question:
Go to the header file where the protocol is defined, and choose
"Generated Interface" from the "Related Items" popup in the top-left
corner. That will show you the exact Swift method signature that you
have to implement.

Related

How to implement objective C protocol in swift class?

#protocol LibraryPaymentStatusProtocol <NSObject>
#required
-(void)paymentStatus:(NSString*)message;
#optional
-(void)onError:(NSException *)exception;
-(void)tryAgain;
-(void)cancelTransaction;
#end
This protocol I want to implement in my swift class. This protocol is present in BDViewController.h file which is inside the Library. I imported the library successfully in my swift project not getting access to this protocol. What is the additional thing I am missing? Thanks in advance.
In my Swift class, I simply want to implement like normal protocol like below
class mainTabViewController: LibraryPaymentStatusProtocol {
// MARK : - Payment status protocol method
func paymentStatus(_ message: String!) {
}
}
Add this line in the bridging header
#import "BDViewController.h"
maybe it will solve your problem. Let me know if it is helpful.
You need to create a Swift bridging header and include your Objective-C file there.
// <ProjectName>-Bridging-Header.h
#import "BDViewController.h" // In which you have written LibraryPaymentStatusProtocol
Then you can implement the required method as
func paymentStatus(_ message: String?) {
}

Pass #protocol type in Swift

I have an Objective-C method which takes Protocol* type as parameter.
How can I invoke this method in Swift.
Example:
// In Objective-C
#protocol AProtocol <NSObject>
#end
#interface MyClass : NSObject
+ (id)proxyWithProtocol:(Protocol*)protocol;
#end
// I can call this method with a protocol as parameter
[MyClass proxyWithProtocol:#protocol(AProtocol)];
If I want to use MyClass in Swift by bridging. How can I pass a protocol defined in Objective-C to proxyWithProtocol method. Can I even pass a protocol defined in Swift to this method?
You would pass the Objective-C protocol in like so:
MyClass.proxyWithProtocol(AProtocol)
If you wanted to pass in a Swift protocol, you would have to expose that protocol to Objective-C:
#objc protocol MyProtocol {
func someGreatFunc()
}
// ...
MyClass.proxyWithProtocol(MyProtocol)
In Swift 3, depending on the way the class is bridged to Swift, your function might look like this:
MyClass.proxy(with: AProtocol)
MyClass.proxy(with: MyProtocol)
Although the compiler isn't happy with the location of "with" and may complain.

What is the correct way of accessing a Swift Delegate from Objective-C?

Environment: Xcode 6.1.1 & Xcode 6.2 Beta
Greetings:
I need to publish a NSString within a Swift doc from a neighboring Objective-C doc within the same project. For example, display "Hello World" generated in Objective-C upon a Swift page. I've made a proof-of-concept demo; based on feedback.
I'm thinking of using an ObjC --> Swift delegate via a protocol pattern as shown below:
Note: the Swift file is the delegate.
Here I'm calling the delegate method in Swift, from Objective-C:
#pragma mark - Action methods
- (IBAction)sendDelegateAction:(UIButton *)sender {
[_delegate radiusString:#"Hello World"];
}
I've instantiated the Objective-C file to link the delegate to the instance (I hope I got it right):
let geo32Controller = MyObjCTableViewController()
geo32Controller.delegate = self
So far, the compiler complained that the Swift protocol couldn't be found.
Here's the protocol (declared in Swift):
#objc protocol DiscoveryContributeProtocol {
// optional
func radiusString(radiusString:String)
}
And here's the delegate reference to that protocol in the Objective-C header file:
#interface MyObjCTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id<DiscoveryContributeProtocol> delegate;
#end
However, the compiler can't find the protocol:
BTW: when I put the bridge reference in the ObjC's header file, I get a compiler error:
Two Questions:
Do I have the correct pattern (did I instantiate the ObjC correctly) ?
How do I make the Objective-C portion see the Swift protocol for the delegate link?
You have the right idea, but have a few bugs that are preventing this from working.
You've declared Geo32Boundaries as conforming to the DiscoveryContributeProtocol, but it doesn't need to and doesn't actually implement it, it only has a property that conforms to that protocol. That's the source of the "Method 'radiusString:' not implemented" error:
#interface Geo32Boundaries: UIViewController // <-- that's all you need
You're setting the delegate incorrectly -- the code you have there looks like it's trying to set a class instance of Geo32Boundaries to self, but you're also trying to call it like a function. You'll need to set the delegate on a the instance of the Geo32Boundaries view controller that is being presented to the user. I don't know where that code lives, so I can't give a great example, but it'll be something like:
geo32Controller.delegate = self
Lastly, though not a bug, your protocol should really be called DiscoveryContributeDelegate -- we usually don't use "protocol" in the protocol name.

Swift protocol in Objective-C class

I wrote SearcherProtocol in Swift and need to implement an Objective-C class FileSearcher which has to use this protocol.
So I tried this:
#import <Foundation/Foundation.h>
#interface FileSearcher : NSObject <SearcherProtocol>
// ... class content
#end
The compiler tells me
Cannot find protocol declaration for 'SearcherProtocol'
The corresponding bridged header file (modulename-Swift.h) is being imported within FileSearcher.m.
Importing SearcherProtocol into FileSearcher.h throws another compiler error: module name-swift.h file not found
Does anybody have any clue what I'm doing wrong?
I'm using Xcode 6 Beta 5.
Edit
Here is the protocol declaration in Swift:
#objc protocol SearcherProtocol
{
var searchNotificationTarget: SearchCompletedProtocol? { get }
var lastSearchResults: [AnyObject] { get set }
func search(searchParam: String, error: NSErrorPointer) -> Bool
}
And the SearchCompletedProtocol:
#objc protocol SearchCompletedProtocol
{
func searchCompletedNotification(sender: AnyObject!)
}
There are two common reasons for this occuring:
Getting the module name wrong, see my answer.
Having a circular reference - see mitrenegades answer below.
1. Get the module name right:
If both the swift protocol and and Objective C are in the same project then according to apple you should just need to make sure you get the correct module name.
For Xcode6 beta 5 you can find it under BuildSettings->Packaging->Product Module Name
A common mistake would be to think that each swift file/class gets its own file, but instead they are all put into one big one that is the name of the project.
A further mistakes are if the module name has spaces, these should be replaced with underscores.
Edit:
With your protocol I created a test project called 'Test' which compiles perfectly and it has the files:
TestObjClass.h
#import <Foundation/Foundation.h>
#import "Test-Swift.h"
#interface TestObjCClass : NSObject <SearcherProtocol>
#end
TestObjClass.m
#import "TestObjCClass.h"
#implementation TestObjCClass
#end
TestProtocol.swift
import Foundation
#objc protocol SearcherProtocol
{
var searchNotificationTarget: SearchCompletedProtocol? { get }
var lastSearchResults: [AnyObject] { get set }
func search(searchParam: String, error: NSErrorPointer) -> Bool
}
#objc protocol SearchCompletedProtocol
{
func searchCompletedNotification(sender: AnyObject!)
}
2. Avoid circular reference:
Mitrenegades answer explains this, but if your project needs to use the explicit objc class that uses the swift protocol, (rather than just using the protocol) then you will have circularity issues. The reason is that the swift protocol is defined to the swift-objc header, then to your obj-c class definition, which then goes again to the swift-objc header.
Mitrenegades solution is to use an objective-c protocol, is one way, but if you want a swift protocol, then the other would be to refactor the code so as to not use the objective-c class directly, but instead use the protocol (e.g. some protocol based factory pattern). Either way may be appropriate for your purposes.
When you have
#import "moduleName-Swift.h"
in the .h file that you want to be a delegate, and you have that .h file also in the bridging headers file, there's a circular reference that causes the moduleName-Swift.h to fail compilation. for #james_alvarez's test project, it's probably working because you don't need to include TestObjClass.h into the bridging header.
The best way for me to combine objc files that need to be the delegate for a class written in swift, but that also needs to be included in the bridging header so other swift files can access this objc class, is to create a separate protocol file in objc:
MyProtocol.h:
#protocol MyDelegate <NSObject>
-(void)didDoThis;
-(void)didDoThat;
#end
ViewController.h:
#import "MyProtocol.h"
#interface ViewController : UIViewController <MyDelegate>
MyProject-Bridging-Header.h
#import "MyProtocol.h"
#import "ViewController.h"
I know this was a long time ago, but I just struggled with the same problem when adding a protocol to my Swift code, and it wasn't being added to the -Swift.h header file, hence "Cannot find protocol declaration"
The problem was my protocol wasn't marked as Public. I changed my protocol from this:
#objc protocol MyProtocol { //etc.... }
to this:
#objc public protocol MyProtocol { //etc.... }
I'm still not entirely sure why I need 'Public' but nobody else seems to, but hey it works...
Make sure you are including the auto generated Swift header in your ObjectiveC file. It will have the same name as your project module followed by -Swift.h.
For instance if your Project Module is MyTarget, then you would use:
#import "MyTarget-Swift.h"
If you are typing in the import into your Objective C file, it will not autocomplete. You can verify that you have the correct file by Command-clicking on the header after typing it in.
You can do the conformance part from Swift side 😁
So you have a swift protocol and want to make an Objective-C type conforms to it,
Swift Side
Add #objc to your protocol to make it visible to Objective-C world.
#objc protocol IndianCooking {
func cookChicken()
}
Objective-C Side
In the implementation .m file you do the following:
#import "YourProject-Swift.h"
#interface Cheef ()<IndianCooking> {
}
and in the header file .h add the method
cookChicken()
Import delegate as like this in .h file
#protocol AnalyticProtocol;
and add this in to .swift file
#objc public protocol AnalyticProtocol {
}
Try adding #import "Product_Module_Name-Swift.h" to your Product_Module_Name-Prefix.pch file. That fixed it for me, plus you will now have access to your swift files from any objc file.

Declaring method prototypes in header and implementation

I am learning object orientated programming from the online Stanford courses there is a part I am unsure of regarding declarations. I thought that you must always declare the prototype in the header and then write the code in the implementation file, but the professor wrote a method in the implementation without a declaration prototype in the header file, how come?
Also, may someone please clear the difference between private and public and if the method without a prototype is public or private? The method without a prototype is not from a super class.
That is a perfectly legal way to declare methods that are not to be used outside the class implementation itself.
The compiler will find methods in the implementation file as long as they precede the method in which they are used. However that will not always be the case, as the new LLVM compiler allows methods to be declared in any order and referenced from a given file.
There are a couple of different styles for declaring methods inside an implementation file:
//In the Header File, MyClass.h
#interface MyClass : NSObject
#end
//in the implementation file, MyClass.m
//Method Decls inside a Private Category
#interface MyClass (_Private)
- (void)doSomething;
#end
//As a class extension (new to LLVM compiler)
#interface MyClass ()
- (void)doSomething;
#end
#implementation MyClass
//You can also simply implement a method with no formal "forward" declaration
//in this case you must declare the method before you use it, unless you're using the
//latest LLVM Compiler (See the WWDC Session on Modern Objective C)
- (void)doSomething {
}
- (void)foo {
[self doSomething];
}
#end
If you write the method in you header file it is public and accessible for other classes / objects. If you do not declare it in the header file the method is a private method meaning that you can access it internally in you class but no other class can use this method.