In Swift project contains,
CartViewModel.swift
#objc public class CartViewModel: NSObject {
let apiService: APIService
var alertMessage: String? {
didSet {
self.showAlertClosure?()
}
}
var isShow: Bool = false {
didSet {
self.updateStatus?()
}
}
#objc public var dataCount: Int {
return models.count
}
}
ListViewController.h
NS_ASSUME_NONNULL_BEGIN
#class CartViewModel;
#interface ListViewController : UIViewController
#property (nonatomic, strong) CartViewModel *viewModel;
#end
NS_ASSUME_NONNULL_END
ListViewController.m
NSLog(#"%#", self.viewModel.dataCount);
// Accessing any property gives this error
Property 'dataCount' cannot be found in forward class object 'CartViewModel'
If you want to be able to access properties of a class inside the implementation file of an Objective-C class, you need to import the header of the class. Simply forward declaring the class by doing #class YourClass only makes the type itself visible, but it doesn't expose its properties/methods.
Since Swift files don't have headers, you need to import the Swift header of your module.
So in your ListViewController.m do
#import <YourModule/YourModule-Swift.h>
In your Objective-C implementation you need to import the Xcode-generated header file for Swift. Add this to your imports replacing ProductModuleName with your target's name:
#import "ProductModuleName-Swift.h"
Just in case anyone else comes here with this problem; used #class and imported the Swift header but the problem is not solved yet... Double check you have made your Swift class public and not internal
Related
I'm a new comer into Objective-C and swift and I need to make changes to an existing code base.
I have added a protocol defined in Swift
File AController.swift
#objc
protocol MyDelegate: NSObjectProtocol {
func saveTapped()
func discardTapped()
}
#objc
class AController: UIViewController, BcAiDelegate {
...
}
The protocol is used as a member is BController.h
and the code compiles and builds w/o issues
// imports are placed here
// #class declarations are here
#protocol MyDelegate;
...
#property (nonatomic) MyDelegate* myDelegate;
#property (nonatomic) BOOL myFlag;
And then I try to access this field in CController.swift
#objcMembers class CController: UIViewController {
...
func doSomething() {
let bController = BController()
// When I try to access `myDelegate` it isn't recognized as a member of the controller
// I get an error message that says - Value of type 'BController' has no member 'myDelegate'
bController.myDelegate = nil
bController.myFlag = true // This lines works fine
}
...
}
If I look at the swift 5 interface generated from BController.h I can see the following definitions:
...
open var myDelegate: UnsafeMutablePointer<Int32>!
open var myFlag: Int33
...
I tried created new initializers in BController.h, one that receives a Bool and the other that receives MyDelegate - and the behavior is the same, the one with Bool is accessible in CController.swift and the other isn't
Obviously I'm missing some part - but what?
I've been having trouble with using Swift in an ObjC framework in iOS. My framework has Objective-C code, which I want to call Swift code from.
I think I have created the bridging properly, I'll show below what I've done.
MySwiftFile.swift :
open class MySwiftFile: NSObject {
var varDummy : RandomType? = nil
open func setupDummy(param1 : RandomType1) {
varDummy = RandomType(p: param1)
}
}
MyObjCFile.m :
#class MySwiftFile;
#import "MyFramework/MyFramework-Swift.h"
#interface A : NSObject<...>
#property(atomic) MySwiftFile *mySwiftFile;
.....
#end
#implementation Aclass
......
#end
#interface B ()
....
#property(readonly, nonatomic) A *refA;
#end
#implementation B
....
- (void)methodCalledSomewhere:(RandomType1 *)type {
....
refA.mySwiftFile = [[MySwiftFile alloc] init];
[refA.mySwiftFile setupDummy: type]; <====THIS LINE PRODUCES THE ERROR
}
....
To sum it up, I want to init the property and call a function of a Swift object from ObjC code. Xcode seems to recognize MySwiftFile as a valid type, then how come it does not allow me to call the "setupDummy" method?
The errors are 2:
No visible #interface for 'MySwiftFile' declares the selector 'setupDummy:'
No visible #interface for 'MySwiftFile' declares the selector 'setupDummy'
First problem is that you forget to expose it to the Objective-C. Either add #objcMembers to expose everything ...
#objcMembers
open class MySwiftFile: NSObject {
...
}
... or just add #objc to your setupDummy function ...
#objc
open func setupDummy(param1: String) {
...
}
Second problem is about how the function name is translated to Objective-C ...
#objc func setupDummy(param1 : RandomType1) -> setupDummyWithParam1:
#objc func setupDummy(_ param1: RandomType1) -> setupDummy:
#objc(setupDummy:) func setupDummy(param1: String) -> setupDummy:
... which means that adding just #objc wont work. You have to either change the Swift function signature or use setupDummyWithParam1: in your Objective-C or keep the Swift function signature & use #objc(<name>) to change the Objective-C selector.
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.
I am currently trying to create my own custom iOS framework using Swift 2.3. I have a test iOS app, written in Obj-C, and it embeds + links to the custom framework.
The framework has multiple classes, two of them resembling the below:
public class Constants: NSObject
{
private static let sharedInstance = Constants()
override init() { }
public static let CONST_VALUE1 = "somevalue1"
public static let CONST_VALUE2 = "somevalue2"
}
and
public class RandomUtils: NSObject
{
private static let sharedInstance = RandomUtils()
override init() { }
public static func randomFunction(someValue: String) -> String?
{
return someValue
}
}
The RandomUtils class has no problems being seen and used in the test app. The Constants class however, cannot seem to be found, despite it being referenced to in the SDK-Swift.h header:
SWIFT_CLASS("_TtC6sdk9Constants")
#interface Constants : NSObject
+ (NSString * _Nonnull)CONST_VALUE1;
+ (NSString * _Nonnull)CONST_VALUE2;
#end
SWIFT_CLASS("_TtC6sdk16RandomUtils")
#interface RandomUtils : NSObject
+ (NSString * _Nonnull)randomFunction:(NSString * _Nonnull)someValue;
#end
In my test app, I am importing the framework umbrella header file in my ViewController's header file as such:
#import <UIKit/UIKit.h>
#import <SDK/SDK-Swift.h>
#interface TestVC : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *txtValue1;
#end
Attempts to use the Constants class
NSLog(#"%#", [Constants CONST_VALUE1]);
result in this error message
Use of undeclared identifier 'Constants'
Does anyone have any idea what I could be doing wrong?
After some trial and error, I resolved the issue by placing the .framework directly into the test app folder.
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.