Strange behavior of Obj-C Bridging Header and main function - swift

I converted my app long ago from Obj-C to Swift. During conversion, I needed a Bridging-Header file.
Now I realized that this file is still defined for one target, although the project uses now only Swift files.
Thus I thought I can simply delete Target/Build Settings/Objective-C Bridging Header.
However, the project then no longer builds.
The bridging header file contains (for historical reasons, since I used earlier the Obj-C version of the StoreKit, but no longer) only a single entry:
#import <StoreKit/StoreKit.h>
If this entry is out commented, I get the error
Cannot find 'UIApplicationMain' in scope
in my main file that contains essentially only
let appDelegateClassName: String?
if !ProcessInfo.processInfo.isTesting {
// No unit test. Use the normal app delegate.
appDelegateClassName = NSStringFromClass(AppDelegate.self)
} else {
// Unit test. No app delegate is used.
appDelegateClassName = nil
}
let args = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, appDelegateClassName)
I assume, I don't need a Bridging-Header, if I don't have any Obj-C files. So why do I get this error when I simply out comment #import <StoreKit/StoreKit.h>? And how do I get rid of all old Obj-C traces?

UIApplicationMain is defined in the UIKit framework, so you need to add
import UIKit
to the main.swift file. With
#import <StoreKit/StoreKit.h>
in the bridging header file it happens to compile because StoreKit.h includes SKOverlay.h, which in turn includes UIKit.h (when compiled for iOS).

Related

Can't get tnoodle-lib-objc working in swift

I am coding a speedcubing timer and I am trying to get the tnoodle-lib library (link here) working with my Xcode project. So far I have managed to import everything I think, but I don't know how to actually generate the scrambles with it. Can somebody please help?
Here is what I have going on:
in my Header.h bridging file:
#ifndef Bridging_Header_h
#define Bridging_Header_h
#import "org/worldcubeassociation/tnoodle/scrambles/PuzzleRegistry.h"
#import "org/worldcubeassociation/tnoodle/scrambles/Puzzle.h"
#import "org/worldcubeassociation/tnoodle/svglite/Svg.h"
#import "org/worldcubeassociation/tnoodle/svglite/Dimension.h"
NSString * const SCRAMBLE = "/("NSSOrgWorldcubeassociationTnoodleScramblesPuzzleRegistry_get_THREE())"
#endif /* Bridging_Header_h */
In my project build settings I imported the tnoodle-lib-objc stuff like it said on the github page. What I can't figure out is how to actually use this to generate scrambles because it seems like you would do that in java, and I don't know how to transfer it from java to swift.
I think it's good to understand what the bridging header does. As we're using Objective-C code, we need to "bridge" this code so it can be used in Swift – this is the purpose of the bridging header. It should only act as an intermediate for the Objective-C code we need, which are the import statements.
Your bridging header should look something like this, for a very simple bare bones approach:
#ifndef Bridging_Header_h
#define Bridging_Header_h
#import "org/worldcubeassociation/tnoodle/scrambles/PuzzleRegistry.h"
#import "org/worldcubeassociation/tnoodle/scrambles/Puzzle.h"
#endif
This gives us access to the tnoodle Puzzle and PuzzleRegistry components.
From here, in our Swift code – anywhere in your main program, you should be able to call the scrambler. For example:
OrgWorldcubeassociationTnoodleScramblesPuzzleRegistry.TWO.getScrambler().generateScramble()
.TWO is the scramble type, could be .THREE, SQ1, any tnoodle scramble type. If you refer to the tnoodle repo, you can see how they've defined their Puzzle, with methods.
Each "puzzle type" has a .getScrambler() method, which returns an OrgWorldcubeassociationTnoodleScramblesPuzzle object, where .generateScramble() can then be called. This will generate a scramble for for the puzzle type you've chosen.
Be wary that the returned object is an Optional, you will need to unwrap to obtain the String.

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).

Swift : import UIKit in each subclass?

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.

Header/main files in Objective C

I am reading about iOS programming and I bought the Programming iOS 4 book. There is a introductory part where among several things "Files" is mentioned.
I don't understand how the source files is put together. You have a header file with function declarations, then you have a corresponding file with the function definitions.
Let say you have a Car.h and Car.m & Person.h and Person.m.
Now, if you want to use the Car in the Person class you would import only the Car.h file. How is this sufficient? I don't understand the sequence it put together and builds a program. (Not thinking about the technical stuff, just h/m files.)
The .h or "header file" contains the interface.
The .m or "implementation file" contains the implementation.
Each implementation file is also called a "compilation unit" because the compiler compiles each one separately. Within each compilation unit, the compiler needs to know about types and methods. All it needs to know about a class to create the right code is information about the methods it implements.
So let's imagine you have these files:
Car.h
#import <Foundation/Foundation.h>
#interface Car : NSObject
- (void)drive;
#end
Car.m
#import "Car.h"
#implementation Car
- (void)drive {
NSLog(#"I'm driving!");
}
#end
Person.h
#import <Foundation/Foundation.h>
#class Car;
#interface Person : NSObject
#property (nonatomic, strong) Car *car;
- (void)start;
#end
Person.m
#import "Person.h"
#import "Car.h"
#implementation Person
#synthesize car;
- (void)start {
[car drive];
}
#end
Now when the compiler does its business, it compiles both Car.m and Person.m into Car.o and Person.o respectively. [These then get linked into the final binary, but that's beyond the scope of this question for now].
When it compiles Person.m, the compiler doesn't need to know how - (void)drive of Car is implemented, but it does need to know that it exists, that it is a method that takes no arguments and returns nothing. It doesn't care about the implementation, just that it exists. So you just need to #import the header file of Car to tell the compiler about the methods that exist on Car. The compiler knows that the implementation exists, because you've told it so, and then later on the linker will do it's business to correctly wire up the method call to the correct implementation. How the linker actually does that is a huge topic and I encourage you to go and read about it separately if you don't already understand it.
Note that it's the same for all of the standard NS classes that you use such as NSObject, NSString, etc. You just need to #import Foundation.h from the Foundation framework which tells the compiler about what these classes are and what methods are defined on them.
Creating an executable from a set of source code files is a two stage process.
Firstly, all of the .m files are individually compiled with the Objective-C compiler. This turns each one into a .o file which is an object code file. However, if the code in a .m file refers to things that are defined in other .m files, the compiler does not know about these so it just leaves unresolved references in the .o file.
The second stage is called linking. This takes all the .o files and combines them into an executable. When the linker finds unresolved references in one .o file, it checks all the others to resolve the reference.
Header files allow the compiler to have some information from outside the particular .m file it is currently compiling. So if you have two classes Foo and Bar they are conventionally defined in files Foo.m and Bar.m In order for the compiler to know what class Bar looks like when it is compiling Foo.m we put class Bars interface declaration in a header file (conventionally Bar.h) and import it into your .m file. If you see the line
#import "Bar.h"
it is literally as if the compiler has copy-pasted the entire header file into the source code file before compiling it.
What language have you been using until now? Many languages do it this way including c and c++. The m files are compiled into an actual program, and the h files provide a list of ways to interact with it. While you can still call the methods if you interact with the objective c runtime, the compiler will not guarantee their existence unless they are in the h file.
Now, I say guarantee, but if you dont provide an implementation in the m file, the sibling to the compiler, the linker will have a fit. It will try to make a jump into another m file based on its h file only to tragically discover that it is not there.
The benefits of splitting like this is that you can compile your source into a library and distribute it along with the h files and another application can use it without having the implementation source code.
In summary the m files compile into a lost island of bits and the h files are the map to get around it. If something is on the map that doesnt exist then you will get lost. If something exists but is not on the map then you will have a lot of trouble finding it.
Header files specify what messages ("methods" in other languages) can be passed to a class. That's all the compiler needs to know to compile your code; the linker will eventually wire everything up, so to speak, using the *.m files.
The compiler will handle that for you.
As you stated, a header file contains just the declarations.
Its like a interface to the actual code and that is the only thing Compiler needs to know to fetch the rest.

Why is XCode freaking out?? I.e., Xcode says methods & properties do not exist but they do!

XCode has been acting really, really strange recently. It is telling me that various classes' methods and properties do not exist - but they do! This is happening both with a custom class, and a Core Data class. I have declared all of the methods and properties, including all the necessary #synthesize calls, and have predeclared the classes using #class in the files which use them and included the .h files, but when I try to access the methods & properties - it throws errors or warnings, along the lines "No '+newMatrix' method found", "'Collection' may not respond to '+newMatrix'", and "Request for member 'isLanguage' in something not a structure or a union." These have all be declared properly - what could be causing XCode to choke?
Check whether they are in the list of files being compiled. You might not have added the files.
For Core data did you include the Framework?
Make sure you're using the right import statements - these two are very different:
#import <SDKLibrary.h>
#import "CustomClass.h"