Swift : import UIKit in each subclass? - swift

In objective-C we can do like this:
a. Importing a file in super class
#import "MyAwesomeClass.h"
#interface MySuperViewController : UIViewController
#end
#implementation MySuperViewController
- (void)viewDidLoad {
[super viewDidLoad];
//MyAwesomeClass allocated, initialized, used
MyAwesomeClass *awesomeClass = [MyAwesomeClass new];
}
#end
b. Using the file imported in superclass, in subclass without re-importing it
#interface MySubViewController : MySuperViewController
#end
#implementation MySubViewController
- (void)viewDidLoad {
[super viewDidLoad];
//No compilation error, since MyAwesomeClass already imported in superclass
MyAwesomeClass *awesomeClass = [MyAwesomeClass new];
}
#end
Trying to do the same thing in swift gives compilation error:
a. importing UIKit in MySuperViewController
import UIKit
class MySuperViewController : UIViewController {
#IBOutlet weak var enterPrice: UITextField!
}
b. Declaring and using an object of UITextField without importing UIKit in MySubViewController
class MySubViewController: MySuperViewController {
// compilation error at below line
#IBOutlet weak var myButton: UIButton!
}
Is there any way we can avoid re-importing UIKit in above scenario? Please suggest.

Short answer:
Yes. It's my understanding that you need to import all the frameworks you need in each Swift file in your project (it is a file-by-file requirement, not class by class. If you define 2 classes in a single file, you only need one import at the top of the file.)
The #import/#include statements in C are preprocessor directives. It is as if the code in the included file is copy/pasted at the location of the include. If you include a header in your superclass's header, the superclass's header now contains the expanded contents. So when you include the superclass header in your subclass, the system framework headers are included as part of the superclass header.
Swift works a little differently.

If you use any objective-C class in your Swift project and import UIKit in that class, you don't actually have to use the import UIKit directive anywhere else in your project!
So basically:
Drag and drop your objective-C .m and/or .h file into your project. Choose copy files if necessary. Make sure all the dependencies are sorted for that file.
(optional) Xcode should prompt you to create a bridging header, if it does, move on to step 4... But if it doesn't, you have to add a header file and call it YourProjectName-Bridging-Header.h.
(optional) If you manually added it, go into Project Settings and search for Swift Compiler. In the General section, there is a place to put the path to the file you just created.
In the bridging header import the objective-c class(es) to your project by doing #import "MyClass.h"
That's it! You are now free to delete your import UIKit statements from every file. It's worth noting that stylistically and for code reuse purposes, it's better to have all of the imports in every file so everyone can see at a glance what dependencies there are, but when you are creating ViewControllers and such I think it's kind of silly to have to import UIKit in every single file. Everyone knows it has a dependency on UIKit and chances are you won't be reusing the UI in another project anyway.

Related

Receiver type '' for instance message is a forward declaration (but header is imported in .m)

I've looked at a lot of posts on this and it usually seems to revolve around missing an import in the .h or the .m
In my case I am trying to import a swift objective C function but I believe the .h, .m and swift files are configured correctly (as is the generated swift-header).
My Swift class is flagged as #objc and extends NSObject.
When I import the class in the .h using forward declaration, and in the .m using the MyApp.h import, it can see the class. However, it cannot see the method I want and it gives me the error Receiver type 'class' for instance message is a forward declaration.
When I check the generated header file, the method is generated there (and the method is flagged as an #objc and returns an #objc compatible value).
Can you suggest what might be causing this issue?
Here is a reference of what my code is like:
Swift
#objc class ObjcHelper: NSObject {
#objc static let shared = ObjcHelper()
#objc public func getObjcFromNSString(nsString: NSString) -> ObjcType {
return ObjcType()
}
}
In the .h for the objective c file I want to use it in:
#class ObjcHelper
And in the .m I am importing the app header
#import <App-Swift.h>
When I try to use the code in the .m file the compiler can see this part fine:
[ObjcHelper shared] // Compiler sees this fine!
But if I try to call the method it doesn't autocomplete or find it even if I type it in.
If I look in the generated header, I see the method is here like so:
SWIFT_CLASS("_TtC7ObjcHelper")
#interface ObjcHelper : NSObject
SWIFT_CLASS_PROPERTY(#property (nonatomic, class, readonly, strong) ObjcHelper * _Nonnull shared;)
+ (\ObjcHelper * _Nonnull)shared SWIFT_WARN_UNUSED_RESULT;
- (enum ObjcType)getObjcFromNSStringWithNsString:(NSString * _Nonnull)nsString SWIFT_WARN_UNUSED_RESULT;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
The code I expect to work that doesn't is as follow (and which generates the error):
ObjcType value = [[ObjcHelper shared] getObjcFromNSStringWithNsString: #"abc"]];
The issue is rather nuanced but it seems to have been solved.
In my project there are a number of targets and for the ObjcHelper it wasn't targeting one of the targets. I believe what was happening is that even though the bridging objective c helper file was created, there was an issue with a reference missing a 'required' target owner and this error propagates forward as not being able to find the class.
So if you are getting this issue, check to make sure that the Swift class you are trying to bring into objective-c has its target membership set to all the targets it needs (otherwise you might get a misleading error about forward class declaration).

receiver type *** for instance message is a forward declaration

In my iOS5 app, I have NSObject States class, and trying to init it:
states = [states init];
here is init method in States:
- (id) init
{
if ((self = [super init]))
{
pickedGlasses = 0;
}
return self;
}
But there is error in the line states = [states init];
receiver type "States" for instance message is a forward declaration
What does it mean? What am I doing wrong?
That basically means that you need to import the .h file containing the declaration of States.
However, there is a lot of other stuff wrong with your code.
You're -init'ing an object without +alloc'ing it. That won't work
You're declaring an object as a non-pointer type, that won't work either
You're not calling [super init] in -init.
You've declared the class using #class in the header, but never imported the class.
FWIW, I got this error when I was implementing core data in to an existing project. It turned out I forgot to link CoreData.h to my project. I had already added the CoreData framework to my project but solved the issue by linking to the framework in my pre-compiled header just like Apple's templates do:
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
I got this sort of message when I had two files that depended on each other. The tricky thing here is that you'll get a circular reference if you just try to import each other (class A imports class B, class B imports class A) from their header files. So what you would do is instead place a forward (#class A) declaration in one of the classes' (class B's) header file. However, when attempting to use an ivar of class A within the implementation of class B, this very error comes up, merely adding an #import "A.h" in the .m file of class B fixed the problem for me.
I was trying to use #class "Myclass.h".
When I changed it to #import "Myclass.h", it worked fine.
If you are getting this error while trying to use Swift class or method in Objective C: you forgot one of 2 steps Apple defined on this diagram:
Example:
Error shows up in your Test.m file:
Receiver 'MyClass' for class message is a forward declaration
In Obj-C files:
Step 1: check that Test.h has
#class MyClass;
Step 2: find *-Swift.h file name in Build Settings (look for Objective-C Generated Interface Header Name). Name will be something like MyModule-Swift.h
Step 3: check that Test.m imports the above header
#import <MyModule/MyModule-Swift.h>
In Swift file:
Ensure MyClass (or it's base class) inherits NSObject class.
Ensure #objc is before each method you want call from Obj-C.
Also, check Target Membership section (in File Inspector).
You are using
States states;
where as you should use
States *states;
Your init method should be like this
-(id)init {
if( (self = [super init]) ) {
pickedGlasses = 0;
}
return self;
}
Now finally when you are going to create an object for States class you should do it like this.
State *states = [[States alloc] init];
I am not saying this is the best way of doing this. But it may help you understand the very basic use of initializing objects.
Check if you imported the header files of classes that are throwing this error.
Make sure the prototype for your unit method is in the .h file.
Because you're calling the method higher in the file than you're defining it, you get this message. Alternatively, you could rearrange your methods, so that callers are lower in the file than the methods they call.
There are two related error messages that may tell you something is wrong with declarations and/or imports.
The first is the one you are referring to, which can be generated by NOT putting an #import in your .m (or .pch file) while declaring an #class in your .h.
The second you might see, if you had a method in your States class like:
- (void)logout:(NSTimer *)timer
after adding the #import is this:
No visible #interface for "States" declares the selector 'logout:'
If you see this, you need to check and see if you declared your "logout" method (in this instance) in the .h file of the class you're importing or forwarding.
So in your case, you would need a:
- (void)logout:(NSTimer *)timer;
in your States class's .h to make one or both of these related errors disappear.

Importing Confusion with objective C in header File

I am new in iphone development and i have little bit Question.
My Question is When do we use #class and #import in .h (header )file.And if your answer is #class u can create instance but can not.. use its methods and by use of #import in .h file we can access all method and variable of second class .Then My Question is
if #import contains advantage then why many people used only #class in their .h file.
Please anybody have answer then reply asap.Thanks in Advance.
First of all, you're right with your assumption.
As for advantages:
The #class directive is faster, since it only discloses the Name, and the Inheritance to the Namespace (e.g. the Header File). But #import loads everything, so it's slower and means more load on the system. If your Code is a library for another system, its pretty useful if the headerfiles only load the classname (#class)
For an example. You have the class A, and are importing a Headerfile B from a library. B itself wants to use C. If it imports all data in the B Headerfile, it gets bloated, because you would load it too when importing the headerfile into your class A. But it isn't necessary, that your class A knows what Class C is capable of, because only B is using it.
Have you ever encountered cyclic imports,I suppose you are not.
The other answers are also correct but when it comes to cyclic imports the compiler gives error and you have to use #class instead of import.
quick example
//A.h
#import "B.h"
//B.h
#import "A.h"
In this case compiler will give you the error. So you have to use #class in one of the header files to remove cyclic import. It is true that #class is faster than #import but my projects doesn't have large amount of files that it would take hours to compile it :)
OK, trying to be more clear, then. This is what you usually want:
Use #class in your .h file if the header file doesn't need access to anything in the class you're importing (i.e. it only needs to know that the class exists in order to compile).
Use #import in your .m file to get access to the imported class' properties and methods.
An example, where your class Foo needs to use another class you've created, Bar. Bar also has a custom initializer, -initWithCustomValue:.
MyClass.h
#class Bar
...
Bar _instanceVariable;
MyClass.m
#import "Bar.h"
...
_instanceVariable = [[Bar alloc] initWithCustomValue:1];
This would make sure that you're not exposing unnecessary code (i.e. the contents of Bar) to other classes that might be importing MyClass.h.
From the Apple docs:
The #class directive minimizes the amount of code seen by the compiler
and linker, and is therefore the simplest way to give a forward
declaration of a class name. Being simple, it avoids potential
problems that may come with importing files that import still other
files. For example, if one class declares a statically typed instance
variable of another class, and their two interface files import each
other, neither class may compile correctly.

Headers #import versus #class [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
#class vs. #import
In the .h file you can add a class to be seen(dont know what the correct terminolgy for this is) by using
#import "SomeClass.h"
or instead use
#class SomeClass;
I've tried both methods and they both worked. Whats the difference? Should I be using one of the methods and not the other? What is best practice?
#import includes the content of the header in the source.
Thus, every declaration which is in the imported header is also imported.
#class only declares to the compiler that the given class exists, but does not import the header itself. It is called a forward declaration, as you only declares to the compiler that the class exists before defining it in details (telling which methods it implements and so on)
Consequences:
When using #import in your .m file, if the header is modified, it will trigger the recompilation of the .m file that #import it on next compilation. Instead, if you use #class, your .m does not depend on the header and if the header is modified, the .m file is not recompiled.
Using #class also avoid cross-imports, e.g. if the class A references class B and class B references class A, then you can't #import "A.h" in B.h and #import B.h in A.h in the same time (it would be an "import infinite loop")
Using #class only declare that a class exists and does not tell the compiler which methods the class responds to.
This is why usually the best practice is to forward-declare the class using #class A in the header (.h) files that references class A, just so that the compiler knows that "A" is a known class but doesn't need to know more, and #import "A.h" in the implementation (.m) file so that you can call methods on the objet of class A in your source file.
In addition to avoid import loops, this will also avoid to recompile files if they don't need to, and thus reduce your compile time.
The only exceptions are when the declaration of your class inherits another class, or when it declares that it conforms to a given #protocol (like delegate protocols and so on), because in this particular case, the compiler needs you to #import the whole definition of the parent class or #protocol (to know if your class correctly conforms to this given protocol).
MyClassA.h
// Tells the compiler that "MyClassB" is a class, that we will define later
#class MyClassB; // no need to #import the whole class, we don't need to know the whole definition at this stage
#interface MyClassA : NSObject {
MyClassB* someB; // ok, the compiler knows that MyClassB is a class, that's all it needs to know so far
}
-(void)sayHello;
-(void)makeBTalk;
#end
MyClassB.h
#class MyClassA; // forward declaration here too
// anyway we couldn't #import "MyClassA.h" here AND #import "MyClassB.h" in MyClassA.h as it would create an unsolvable import loop for the compiler
#interface MyClassB : NSObject {
MyClassA* someA; // ok, the compiler knows that MyClassA is a class, that's all it needs to know so far
}
-(void)talk;
-(void)makeABePolite;
#end
MyClassA.m
// import MyClassB so that we know the whole definition of MyClassB, including the methods it declares
#import "MyClassB.h" // thus we here know the "-talk" method of MyClassB and we are able to call it
#implementation MyClassA
-(void)sayHello { NSLog(#"A says Hello"); }
-(void)makeBTalk {
[someB talk];
// we can call the 'talk' method because we #imported the MyClassB header and knows this method exists
}
#end
MyClassB.m
// import MyClassA so that we know the methods it declares and can call them
#import "MyClassA.h"
#implementation MyClassB
-(void)talk { NSLog(#"B is talking"); }
-(void)makeABePolite {
[someA sayHello];
// we can call this because we #import MyClassA
}
#end
PS: Note that if this is a best practice, I know a lot of developers (including myself sometimes ^^) that #import the header it needs in their .h files, instead of only forward-declare it using #class... this is some bad habit — or because these developers doesn't know these subtleties — that you will unfortunately encounter in existing code anyway.
Using #class is called forward declaration. Since usually you don't need to know the specifics of the class in the .h file, this is usually all you need.
Forward declaration prevents you getting into a situation where you import a particular .h file, which says to import another .h file, which says to import the original .h file again, and so on.
The #class forward declaration allows you to have your interfaces behave like interfaces. Meaning: Declare your code.
But this doesn't mean that you can leave out the #import statement. You just moved the responsibility to the implementation to import and make use of it.
Basically it could be seen as an increase in performance as you're not importing any other headers inside your current header.
Important Note: This isn't the case when you're working with delegates.
If you're making use of delegates you always have to have the proper #import statements in place so that the compiler knows which delegate methods are to be implemented by that class.
You might also want to have a look at the following SO question: #class vs. #import

Cocoa class method signature problem

I have a class method but when in header file, it doesn´t want to compile:"expected a type"
+(void)addCommentSectionIntoMonitoringReport:(NSString*)DBCommentName:(NSString*)keyName:(NSManagedObject*)outerObjectToDB:(NSDictionary *)monitoring_report;
but when I add:
#import "AppDelegate.h"
it works fine. Can you tell me why? What does AppDelegate has to do with NSManagedObject type?
Are you #importing <CoreData/CoreData.h> in your .h?
First, your method is a class method (+ sign).
Second, your method signature requires knowledge of NSManagedObject and NSDictionary.
Since those are the only elements requiring "external" knowledge, I'd wager AppDelegate.h contains imports of one of these.
I just tested your method by pasting it into one of my classes that imports headers that import CoreData. It builds fine, except it's marked as "unimplemented" ;)