I have been mixing Swift and Objective C just fine but I'm having issues gaining access to a Swift class from the HEADER file of Objective C. I can do so successfully in the .m file.
Within the .h file I import the Xcode generated file that has the format "Appname-Swift.h". However, in doing so I get the message that the file is not found. I can do this same import in my .m file with no issue. However, I need it in the .h file as I reference a Swift class that I need access to with public API.
How can I make use of the Swift class from the .h portion of Objective C?
Example:
#import <UIKit/UIKit.h>
#import "MyApp-Swift.h"
#interface SelectedContactsVC : UIViewController
#property (nonatomic,strong) MapVC *mapVC;
#end
MapVC above is a Swift class.
Move #import "MyApp-Swift.h" to .m file.
And make your .h file as:
#import <UIKit/UIKit.h>
#class MapVC;
#interface SelectedContactsVC : UIViewController
#property (nonatomic,strong) MapVC *mapVC;
#end
Swift cannot generate "MyApp-Swift.h", if it's imported from Objective-C header, sort of mutual dependency thing maybe.
Related
I started writing Swift extensions on my view controllers. So I have three files right now:
My header file, ViewController.h:
#interface MyViewController : UIViewController
#end
My Obj-C implementation file, ViewController.m:
#interface MyViewController () <UIScrollViewDelegate>
#property (strong, nonatomic) UIScrollView *scrollView;
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] init];
[self customiseScrollView]; //This is Swift method called from Obj-C file
}
#end
And finally, ViewController.swift:
extension MyViewController {
func customiseScrollView() {
}
}
My question is, is it possible to access my Obj-C properties from my Swift implementation file? Every time I refer to it, I got an error:
Value of type 'MyViewController' has no member 'scrollView'
Bonus 1: Can someone also clarify if the Swift component can see what the .m is a delegate of as well. (To clarify, doing scrollView.delegate = self in Swift is a compile error because the Swift file does not realise that the .m file is a UIScrollViewDelegate).
Bonus 2: Can Swift extension file call Obj-C methods that are declared from the .m counterpart?
I think that you can't access private properties from extension. Your scrollView property is in .m file, not .h - which means it's private and it's not visible from extension file.
Solution: move
#property (strong, nonatomic) UIScrollView *scrollView;
to your header file.
You can access internal objc properties and methods if you declare the objc class extension in a separate header and include that header in the bridging header.
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, copy, readonly) NSString *string;
#end
MyClass+Private.h
#import "MyClass.h"
#interface MyClass ()
#property (nonatomic, copy) NSString *string;
#end
MyClass.m
#import "MyClass+private.h"
#implementation MyClass
//...
#end
Project-Bridging-Header.h
#import "MyClass.h"
#import "MyClass+Private.h"
In Swift, private properties are not accessible from another file. This is the meaning of private in Swift. For example:
file1.swift
class MyClass {
private var privateProperty: String = "Can't get to me from another file!"
}
extension MyClass: CustomStringConvertible {
var description: String {
return "I have a `var` that says: \(privateProperty)"
}
}
file2.swift
extension MyClass {
func cantGetToPrivateProperties() {
self.privateProperty // Value of type 'MyClass' has no memeber 'privateProperty'
}
}
A property declared in the implementation of an Objective-C class is a private property. As such, the property cannot be accessed from a Swift extension since this will be necessarily from a different (.swift) file...
You can. All you need is to create a bridging objective C header.
Per Apple Documentation:
To import a set of Objective-C files in the same app target as your
Swift code, you rely on an Objective-C bridging header to expose those
files to Swift. Xcode offers to create this header file when you add a
Swift file to an existing Objective-C app, or an Objective-C file to
an existing Swift app.
Just create a Bridging-Header file and later import your ObjC file in it, like:
#import <objectivec.h>
Later in your swift file:
var a = objectivec()
a.method_from_those_file
For more information read Apple Doucmentation from here.
I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end
why do I see #interface twice( inside .h and .m files in this UIViewController files I've created. the one in the .m seems to be like a constructor. is it?
.h file
#interface BlackTimer : UIViewController
#end
.m file
#interface ViewController ()
#end
usually in the .m file you put all the declarations for private methods
it's a good use to write the word "private" (or something similar) like this:
#interface ViewController (private)
-(void)myPrivateMethod;
#end
The #interface in the .m file is called a class extension. Here is a good link explaining it. Hope this helps.
And here is the Apple documentation on class extensions.
The #interface ViewController () definition in the implementation file (.m) is an anonymous Category. It allows to define ivars, properties and messages your class implements without exposing them to other objects importing your public interface (.h).
Since I had a import-cycle recently, I'm moving all #import statements (concerning my own files) from the header into the corresponding .m-file. I also added #class and #protocol forward-declarations to soothe the compiler. However, I still get he following warning:
Cannot find the protocol definition for 'MyCustomDelegate'.
As I said, there is an #protocol MyCustomDelegate before I use it in the #interface-Block. Interestingly this warning only occurs if the corresponding delegate is declared in another file (whose header is imported in the .m-file).
I read that one solution is to declare the delegate in a separate header file and import that file directly in the header of the class that implements the delegate. Is this really the way to go? Are there any other solutions? I think those delegates already bloated our code enough, now I should go on and even declare an own file for it?
Small sample code to better illustrate the problem:
NewFooController.h
#import <UIKit/UIKit.h>
#protocol NewFooControllerDelegate;
#interface NewFooController : UITableViewController
#property (nonatomic, weak) id<NewFooControllerDelegate> delegate;
#end
#protocol NewFooControllerDelegate
#end
HomeTableViewController.h
#import <UIKit/UIKit.h>
#protocol NewFooControllerDelegate;
// warning points to line below
#interface HomeTableViewController : UITableViewController <NewFooControllerDelegate>
#end
HomeTableViewController.m
#import "HomeTableViewController.h"
#import "NewFooController.h"
#implementation HomeTableViewController
#end
HomeTableViewController.h references the protocol, but it hasn't been declared yet.
If you import NewTaskController.h in HomeTableViewController.h before it attempts to use it, it should solve your problem.
Of course you can then remove the import from HomeTableViewController.m
Not sure if this is "best way", but try import header of class that implement protocol before class header file.
HomeTableViewController.m
#import "NewFooController.h"
#import "HomeTableViewController.h"
#implementation HomeTableViewController
#end
And you can remove protocol declaration in HomeTableViewController.h
#import <UIKit/UIKit.h>
#interface HomeTableViewController : UITableViewController <NewFooControllerDelegate>
#end
How can you use circular #import to strongly typed objects in objective-c
I have an audio controller class, a menu class and a gameview class.
The application delegate sets up these classes and assigns a pointers so:
the menu class is aware of the audio and gameview class
the gameview class has a reference to the audio and menu class
I am using NSObject for the reference to the gameview class from the menu class. This is because the menu class has a reference to the gameview class and has a #import gameview.h declaration. The code won't compile with circular #import
Suggestions please :) ?
#interface MenuViewController : UIViewController {
NSObject *gameref; // weak type here to avoid include of gameview above
AudioController *audioref;
}
and...
#import "AudioController.h"
#import "MenuViewController.h"
#interface GameViewController : UIViewController {
MenuViewController *menuref;
AudioController *audioref;
}
Fisrt, in your .h file use
#class GameViewController, AudioController
and
#class AudioController, MenuViewController
in your .m file use
#import "GameViewController"
#import "AudioController"
and
#import "AudioController.h"
#import "MenuViewController.h"
There is no 'circular reference' problem.
Second, using NSObject instead of the actual classname isn't in any way a weak reference. Also, if you mean #include say #include. If you mean #import, say #import.