Objective-C protocol method invisible in Swift - ios

I'm currently working on some Swift classes in my ObjC project.
The problem I have is the following:
I have this protocol declared in ClassA.h:
#protocol MyProtocol <NSObject>
- (void)complexMethodWithArg1:(id)arg1 arg2:(id)arg2 arg3:(id)arg3;
- (Folder *)currentDestinationFolder;
- (Flow)currentFlow;
#end
Pretty standard stuff.
Now my goal is to have a swift class with a property that is an object implementing this protocol.
So naturally, I add my class to the swift bridging header:
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "ClassA.h"
and declare my property in my swift file under ClassB which is a UIViewController that implement ANOTHER protocol
class ClassB : UIViewController, AnotherProtocol {
var delegate:MyProtocol?
}
Problem here is: I want to call a bunch of my delegate methods in viewDidLoad. It's working for all of them except ONE method that gets not autocompletion and errors the compilation if entered manually:
override func viewDidLoad() {
self.delegate?.currentDestinationFolder() // works great, no problem
self.delegate?.currentFlow() // works great, no problem
self.delegate?.complexMethodWithArg1(arg1: arg1, arg2: arg2, arg3: arg3) // PROBLEM : no autocompletion, error if entered manually !
super.viewDidLoad()
}
I have no idea what's going on, it's not related to optional or required protocol methods, not related to the fact that my delegate property is optional (tried unwrapped).
Has anybody face some similar issue? seems like some kind of bug?

I went ahead and tried to reproduce the problem on an empty project.
MyProtocol.h (taking the declaration from your question and comments)
#import Foundation;
#import UIKit;
#class CAPNavigationBar;
#protocol MyProtocol <NSObject>
- (void)setupNavigationItemInNavigationBar:(CAPNavigationBar *)navigationBar
navigationItem:(UINavigationItem *)navigationItem
inViewController:(UIViewController *)viewController;
#end
CAPNavigationBar.h (just a mock)
#import Foundation;
#interface CAPNavigationBar : NSObject
#end
ViewController.swift
import UIKit
class ViewController: UIViewController {
var delegate: MyProtocol?
override func viewDidLoad() {
super.viewDidLoad()
let capNavigationBar = CAPNavigationBar()
self.delegate?.setupNavigationItemInNavigationBar(capNavigationBar, navigationItem: nil, inViewController: self)
}
}
Bridging header
#import "MyProtocol.h"
#import "CAPNavigationBar.h"
Summary
Everything is working as expected.
You have either a simple typo somewhere or you are not importing correctly all the types into Swift. Especially make sure that you are not importing types only as forward declarations.

Related

How to conform swift class delegate in objective c Class using swift 4?

Suppose there are two class one in swift and other is in objective-c class in same project.
In swift class i declared delegate and i want to set that delegate in objective c class.
I have done this following way ...
import UIKit
#objc public protocol SwiftLoginVCDelegate: class {
}
#objc public class SwiftLoginViewController: UIViewController {
#IBOutlet var txtUserName: UITextField!
#IBOutlet var txtPassword: UITextField!
#IBOutlet var btnLogin: UIButton!
var delegate: SwiftLoginVCDelegate?
override public func viewDidLoad() {
super.viewDidLoad()
self.txtPassword.text = "XYZ"
self.txtPassword.text = "123"
self.btnLogin.backgroundColor = UIColor.blue
}
#objc public func testObjective(){
print("Swift class is integrated in objective c project")
}
the objective c class is
#import "Mediator.h"
#class SwiftLoginViewController;
#protocol SwiftLoginVCDelegate;
#interface LoginMediator : Mediator
#end
Implementation Class is
#import "xyz-Swift.h"
#class SwiftLoginViewController;
#implementation LoginMediator
-(void)onRegister
{
// line 1: [(XYZController*)self.viewComponent setDelegate:self];
line 2 : [(SwiftLoginViewController*)self.viewComponent setDelegate:self];
[objectVC testObjective];
}
If u check the onRegister Method , In line No 1 delegate is set using objective c, which is commented now , but i want to set same delegate in swift 4 , line no 2, when I try to set delegate in swift 4 I am getting following error
No visible #interface for 'SwiftLoginViewController' declares the
selector 'setdelegate';
Note :
One more that i am able to access all the var and function defined in
swift class to objective c Class. I am not able to set the
Delegate in objective c Class which is declared in swift Class.
Can any one has any idea what i am doing wrong in above code ? All inputs are appreciated.
Okay so I've made a sample project in Objective-C and then installed my Swift framework called GPKit, once I made it working, I realized you're not using Cocoapod. So I made a sample Swift class and then used it in my Objective-C class.
First, you need to learn how to properly use a Swift file/class inside your Objective-C class, learn from here: Can't use Swift classes inside Objective-C
And then once you get it working, confirming to a Swift delegate and implementing the functions in that delegate will be easy.
What I can see in your implementation is that you're making a new class and protocol.
#class SwiftLoginViewController;
#protocol SwiftLoginVCDelegate;
Here's the sample code that I made just for this question:
ViewController.m
#import "TestObjcUsingGPKit-Swift.h"
#import "ViewController.h"
#interface ViewController () <CuteDelegate>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CuteClass *newCutie = [[CuteClass alloc] init];
newCutie.delegate = self;
}
- (void)myCuteFunc {
// --- the delegate function
}
#end
CuteClass.swift
import UIKit
#objc public protocol CuteDelegate: NSObjectProtocol {
#objc func myCuteFunc()
}
public class CuteClass: NSObject {
weak var delegate: CuteDelegate?
}
The full sample project on GitHub for you: https://github.com/glennposadas/UsingSwift-In-ObjectiveC
I had the exact same issue as you. I noticed that the generated -Swift.h file did not expose the weak delegate member in my Swift file to the objective-c runtime. You can verify this by doing a command+click on your #import -Swift.h file and search for you Swift class name to see what methods/members are exposed.
To finally fix the issue, I ended up adding the #objc tag to my delegate. In your case it would be:
#objc weak var delegate: SwiftLoginVCDelegate?
After I did that, I observed the -Swift.h file again, and lo and behold my delegate was exposed and I'm able to use it in my objective c file as expected.
Hope this helps.
#Gleen response is fine but you must add #objc to var delegate too
weak var delegate: CuteDelegate?
The issue is coming from using setdelegate instead of setDelegate ( see the lowercase delegate).
Try:
[(SwiftLoginViewController*)self.viewComponent setDelegate:self];
// or
((SwiftLoginViewController*)self.viewComponent).delegate = self;
I was stuck on this until I found a different problem: I had #objcMembers on the Swift class definition, but the protocol definition sits outside of the class definition and needs its own #objc annotation.

Using GTM v5 TAGCustomFunction in Swift

I'm integrating GTM v5 (GTM + Firebase) in a Swift project, and I want to be able to call some methods when tags are triggered. However, it doesn't seem to work with Swift, although similar implementations in Objective C and Android projects did work.
Here's the class conforming to the TAGCustomFunction protocol :
import Foundation
import GoogleTagManager
final class Tags: NSObject, TAGCustomFunction {
func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}
Everything is working well, even though I see these kind of logs:
GoogleTagManager info: Processing logged event: applicationStart with parameters: (null)
But the log I'm printing is not showing…
I'm sure about the configuration of the container since this one is correctly loaded, and I use the exact same container for my Objective C project, in which it works perfectly.
I think TAGCustomFunction needs to have class/method #objc annotations, and the class-level #objc annotation needs to specify the class name, i.e.
import Foundation
import GoogleTagManager
#objc(Tags)
final class Tags: NSObject, TAGCustomFunction {
#objc func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}
After reading this SO post, I suddenly remembered that a Swift project could embed some objective C classes.
The solution to my problem was ridiculously easy to set up once I realized that, eventhough I've never had to do it before.
I created a new Cocoa Touch Class like the following :
Here is the .m :
#import "MyCustomTagClass.h"
#import "MySwiftClass-Swift.h"
#implementation MyCustomTagClass
- (NSObject*)executeWithParameters:(NSDictionary*)parameters {
[MySwiftClass myMethod];
}
#end
And here is the .h :
#import <Foundation/Foundation.h>
#import <GoogleTagManager/TAGCustomFunction.h>
#interface MyCustomTagClass : NSObject <TAGCustomFunction>
- (NSObject*)executeWithParameters:(NSDictionary*)parameters;
#end
Note that I import a header for my Swift class, which is automatically generated by Xcode. Just add -Swift.h after the name of your class to import it, just as I did in the .m example above.
Last, but not least, update your Swift class with #objc annotations at class and method declaration lines :
import Foundation
#objc class MySwiftClass: NSObject {
//...
#objc static func myMethod() {
// do something...
}
}
I hope this helped !

Implementing an Obj-C class in my Swift project

I'm trying to add a webshop to my app. The problem is that the webshop has provided me with an Obj-C framework and I'm only familiar with making iOS apps in Swift.
I've managed to setup an Obj-C bridging header and instantiate the webshop object.
My project looks like this:
When the shop button on my CodeDetailViewController gets tapped this function gets triggered:
#IBAction func shopButtonPressed(sender: UIButton){
let instanceOfShop : Shop = Shop()
instanceOfShop.showShop()
}
My Shop.m looks like this:
#import <Foundation/Foundation.h>
#import <FMShop/FMShop.h>
#import "Shop.h"
#implementation Shop
- (void) showShop {
//SANDBOX
FMShop* shop = [[FMShop alloc] initWithKey:#"4M7HDPAQMY68S"
storeKey:#"C8RSHBH710GK8"
buyerID:nil
sandbox:YES
controller:nil
delegate:nil];
[shop showStore:kShopViewHome ID:nil];
}
#end
The showShop function in this class gets triggered. Nothing however shows up.
The manual on how to implement this webshop is aimed at Obj-C apps. Concerning the delegate it states:
delegate: Please define a class or an interface that implements the delegates. Without such, the Store will not show. Please go to FMShopDelegate to see the supported list of methods and callbacks.
This is what I could find in the framework:
#protocol FMShopDelegate <NSObject>
#optional
-(void)shopWillAppear:(FMShop*)shop;
-(void)shopDidAppear:(FMShop*)shop;
-(void)shopWillDisappear:(FMShop*)shop;
-(void)shopDidDisappear:(FMShop*)shop;
-(void)shopProductInfo:(FMShop*)shop products:(NSArray*)productArray;
-(void)shopHistoryOrderInfo:(FMShop*)shop orders:(NSArray*)orderArray;
-(void)shopPlaceOrderSuccess:(FMShop*)shop orderID:(NSString*)orderID;
-(void)shopOrderUserCancelled:(FMShop*)shop orderID:(NSString*)orderID;
-(void)shopFailed:(FMShop*)shop error:(FMShopErrorType)error;
#end
Can anyone tell me how to connect this final method to the right delegate?
Sounds like if you don't provide a delegate when you instantiate your instance of FMShop, then the show method will not work. In your Shop.m file, you're instantiating the FMShop object with nil as the delegate parameter.
Try having an object conform to the FMShopDelegate protocol, and passing this object as the delegate parameter. You could even have your Shop object conform to the protocol itself, and pass self as the delegate if that would serve your purposes.
You could do this in Swift in a new Shop.swift file, or in the Shop.m file in Objective-C:
Swift:
class Shop: NSObject {
func showShop() {
// I'm guessing on the syntax here, Xcode will help autocomplete the correct functions
let shop = FMShop(key: KEY, storeKey: STOREKEY...)
shop.show(store: kShopViewHome, id: nil)
}
}
extension Shop: FMShopDelegate {
// Implement any of the protocol functions you want here
}
Objective-C:
#interface Shop() <FMShopDelegate> // Conform to the delegate here
#end
#implementation Shop
- (void) showShop {
//SANDBOX
FMShop* shop = [[FMShop alloc] initWithKey:#"4M7HDPAQMY68S"
storeKey:#"C8RSHBH710GK8"
buyerID:nil
sandbox:YES
controller:nil
delegate:self];
[shop showStore:kShopViewHome ID:nil];
}
// Implement any of the protocol methods here
#end
You have to use bridging header for accessing obj-c file in swift
Below is the apple doc to describe how to create & use bridging header
Apple Doc
Use bridging header in your project.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html

File Scope? Swift Delegates and Protocols

I'm working on building a new Swift app roughly based off an old Obj-c app. I'm currently working on the delegates
Here is what my obj-c code looked like in the .h file
#interface MyAppViewController : CustomViewController
#property (nonatomic, weak) id<MyAppViewControllerDelegate> delegate;
#end
#protocol MyAppViewControllerDelegate <NSObject>
- (void)myAppViewController:(MyAppViewController *)controller loggedInStudent: (MYStudent *)student;
- (void)myAppViewControllerWantsSignUp:(MyAppViewController *)controller;
#end
In SWIFT I did:
class MyAppViewController: CustomViewController {
var delegate: MyAppViewControllerDelegate?
protocol MyAppViewControllerDelegate{
func myAppViewController(controller: MyAppViewController, loggedInStudent: MYStudent)
func myAppViewControllerWantsSignUp(controller: MyAppViewController)
I've done a lot of reading and study on this, so I thought I was doing it basically right (totally new to swift though... so)
I'm getting this error though, "Declaration is only valid in file scope" on the protocol MyAppViewControllerDelegate {
I assumed this had something to do with declaring it within the class, so I moved it out, only now my code within the class doesn't recognize the delegate variable I declared..
Any ideas?
Should be this:
protocol MyAppViewControllerDelegate {
func myAppViewController(controller: MyAppViewController, loggedInStudent: MYStudent)
func myAppViewControllerWantsSignUp(controller: MyAppViewController)
}
class MyAppViewController: CustomViewController {
var delegate: MyAppViewControllerDelegate?
}
Although, if you're following a common pattern where the object that owns MyAppViewController is also its delegate, this may cause memory issues. You can use class typing to allow weak delegation like so:
protocol MyAppViewControllerDelegate : class {
func myAppViewController(controller: MyAppViewController, loggedInStudent: MYStudent)
func myAppViewControllerWantsSignUp(controller: MyAppViewController)
}
class MyAppViewController: CustomViewController {
weak var delegate: MyAppViewControllerDelegate?
}
This is slightly limiting because it requires you to use a class for your delegate, but it will help avoid retain cycles :)
From what I see in your source code is that you've declared your protocol inside of your class.
Just declare the protocol outside of the class declaration and you'll be fine.
Update:
The default access level is set to internal which is defined as
Internal access enables entities to be used within any source file from their defining module, >but not in any source file outside of that module. You typically use internal access when >defining an app’s or a framework’s internal structure.
In contrast to Objective-C or C you don't need a forward declaration if the implementation haven't happened before the usage.
Source: The Swift Programming Language, Access Control

Swift Framework <-> ObjC interoperation

I've got a dylib framework with some UIView subclasses made in swift which I've done to use the new #IBDesignable and #IBInspectable stuff.
So lets say I've got a UITextField subclass in there named MyTextField.swift like this:
MyTextField.swift
#IBDesignable class MyTextField: UITextField {
// some properties etc.
// content is irrelevant
}
Now it compiles and works well in InterfaceBuilder so far so good. But what I need to do is import this special subclass into my Objective C implementation of the controller to set a property in code at runtime.
The framework (named myViews) has a header called myViews.h which I am able to import into the controllers header like this:
MyViewController.h:
#import myViews;
#import <UIKit/UIKit.h>
#import "myViews.h"
#interface MyViewController : UIViewController
#property(weak, nonatomic) IBOutlet MyTextField *txtName; // <-- This is the problem!
#end
This is where I'm stuck! So the class MyTextField is unknown from the imported headers. The myViews.h was automatically generated. I've tried to import the bridging-headers in there without success.
myViews.h:
#import <UIKit/UIKit.h>
//! Project version number for myViews.
FOUNDATION_EXPORT double myViewsVersionNumber;
//! Project version string for myViews.
FOUNDATION_EXPORT const unsigned char myViewsVersionString[];
// In this header, you should import all the public headers of your framework
// using statements like #import <myViews/PublicHeader.h>
#import <myViews/MyTextField-Swift.h> // <-- This is what I've tried to import here which doesn't work as well.
Hope anybody may help me out. Thanks in advance.
In your objective-c class, try importing your swift module like this:
#import "<ModuleName>-Swift.h"
Where ModuleName is the name of the module containing the swift code

Resources