In Swift how do I declare a reference to a global pointer that's declared in objective-c code? - cocoa-touch

My App is objective-c origin, with all new files being Swift. My main.m file has:
ConfigMgr* configMgr;
All my objective-c (.m) files have:
extern ConfigMgr* configMgr;
It's a global pointer because everyone needs to access some info about the configuration. (It's my only global and I make no apology for it.) So what do I add to my Swift files to be able to access this instance of this object as well, by using the global, not passing a pointer to every .swift file? (My main concern is to make sure I don't end up instantiating another ConfigMgr object.)

Assuming that the interface of the ConfigMgr class is declared in ConfigMgr.h, include the following in the bridging header:
#import "ConfigMgr.h"
extern ConfigMgr* configMgr;
You should be good to go!

Related

Object C, Public Functions and Classes

Brand new to objective C, have been working on some Swift, converting some reusable files into an importable framework. For swift, I mostly only had to make the classes as well as some functions public so that they could be accessed when imported as a framework, how is this achieved in objective C? (& how are bridging files handled when this is done) Thanks!
In Objective-C, you generally define the #interface in a .h file and include any public methods and properties in that .h file. And then to expose those in your framework, you #include that .h file in the umbrella header. Or, if including this in Swift project, you'd import that .h file in the bridging header.
Define any private properties or ivars inside a private class extension inside the .m file. That keeps them from being exposed in the .h file.

How do .swift files keep the 'privacy' which the .h and .m files did in Obj-C

I have just started to shift from Obj-C to Swift in Xcode.
My question is about the new .swift files which replace the .h and .m files. The answers to this question partially tells me the differences, but I am still puzzled by the issue of 'privacy' with the new .swift file.
If I look up the definition of a class in Obj-C, I will be only be able to view the .h file (#interface) which contains the declaration of the class, its variables and methods. I cannot view the .m file (#implementation) which contains the code which controls how the class works (they are private which stops others from seeing/possibly plagiarising your code, I guess).
However, in Swift, it seems that something such as:
class NewClass: ParentClass {...}
creates a class in one go without the need for 2 sections - the #interface and #implementation. But when I look up the definition of a class (say, SKScene) in Swift, the code for the methods, etc, are still kept private somehow... How is this done?
The same situation in which you only see header files (e.g. a pre-compiled framework) will, with Swift, only make the interface available. The Swift files themselves will not be included in the framework. Instead, Xcode will generate pseudo-interface files that will be visible to users of the framework. You get property definitions and method signatures, but no implementations.

How can I add forward class references used in the -Swift.h header?

I'm integrating Swift code into a large Objective-C project, but I'm running into problems when my Swift code refers to Objective-C classes. For example, suppose I have:
An Objective-C class called MyTableViewController
An Objective-C class called DeletionWorkflow
I declared a Swift class as follows:
class DeletionVC: MyTableViewController {
let deleteWorkflow: DeletionWorkflow
...
}
If I now try to use this class by importing ProjectName-Swift.h into Objective-C code, I get undefined symbol errors for both MyTableViewController and DeletionWorkflow.
I can fix the problem in that individual source file by importing DeletionWorkflow.h and MyTableViewController.h before I import ProjectName-Swift.h but this doesn't scale up to a large project where I want my Swift and Objective-C to interact often.
Is there a way to add forward class references to ProjectName-Swift.h so that these errors don't occur when I try to use Swift classes from Objective-C code in my app?
You can create another header file that forward declares or imports the necessary classes, and then imports ProjectName-Swift.h. For example, create a file named ProjectName-Swift-Fixed.h with the contents:
// ProjectName-Swift-Fixed.h
// Forward declarations for property classes
#class DeletionWorkflow;
// Imports for superclasses
#import "MyTableViewController.h";
#import "ProjectName-Swift.h"
Then, instead of #import "ProjectName-Swift.h" in your codebase, use #import "ProjectName-Swift-Fixed.h.
This is a little silly, but it sounds like your "workaround" is what Apple intended, at least for now. From the interoperability guide:
If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types prior to importing the Swift generated header into the Objective-C .m file you want to access the Swift code from.
In this devforums thread, someone mentioned they already filed a bug in Radar. You probably should too.

Why is Xcode reporting a "defined but not used" warning for my class variable?

I am getting a warning on this line in my header, but I am using the class variable in my implementation (in both class methods and instance methods):
#import <UIKit/UIKit.h>
static NSMutableArray *classVar; // Xcode warning: 'classVar' defined but not used
#interface MyViewController : UIViewController {
This variable is not a class/instance variable. Each time when the header file is included to .m file, the compiler creates a new static variable with scope limited to the file that includes this header. If you're trying to get a class level variable, move the declaration to the beginning of respective .m file.
A static variable has file scope. Since Xcode can't find the variable being used in that file, it sees an unused variable. If you actually want the variable to be accessible from your whole program, make that an extern variable declaration and define it in your implementation. If it's only meant to be used by that class, just move the static variable into your implementation file.
You have placed the classVar outside the interface definition. This will make the compiler think you are declaring a global variable, and as this looks like it is a header file (.h) it will also be created in all files including this header file. I'd guess the warning comes when compiling a file other than MyViewController.m that includes this header file.
EDIT
My suggestion is that you move the classVar into the .m file for MyViewController (miss-interpreted what you where after first)
Here is the correct way to do this:
In the .h
extern NSString *const DidAddRecordNotification;
In the .m
NSString *const DidAddRecordNotification = #"DidAddRecordNotification";

Forward-declare enum in Objective-C

I'm having trouble with enum visibility in an Objective-C program. I have two header files, and one defines a typedef enum. Another file needs to use the typedef'd type.
In straight C, I would simply #include the other header file, but in Objective-C, it's recommended not to use #import between header files, instead using forward #class declarations as needed. However, I can't figure out how to forward-declare an enumeration type.
I don't need the actual enumerated values, except in the corresponding .m implementation file, where I can safely #import away. So how can I get the typedef enum to be recognized in the header?
Most recent way (Swift 3; May 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:
// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);
// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h
typedef NS_ENUM(NSUInteger, XYZEnumType) {
XYZCharacterTypeNotSet,
XYZCharacterTypeAgent,
XYZCharacterTypeKiller,
};
#endif /* XYZCharacterType_h */`
The answer to your question is to either go ahead and import the typedef header file or to use a generic type like NSInteger instead of the enum type.
However, there is more reason to not importing a header file than just compile speed.
Not importing a header file also reduces your inadvertent access to extraneous classes.
For example, say you have a TrackFileChanges class that tracks the file system for changes to a specific file, and you have a CachedFile class that stores cached data from a file. The latter might use a private ivar of type TrackFileChanges*, but for uses of CachedFile, this is simply an implementation detail (ideally, the ivar would be auto-generated with a private property using the new runtime, but thats not possible if you're using the old run time).
So clients that #import "CachedFile.h" probably do not need or want access to TrackFileChanges.h. And if they do, they should make it clear by #importing it themselves. By using #class TrackFileChanges instea of #import "TrackFileChanges.h" in CachedFile.h you improve the encapsulation.
But all that said, there is nothing awrong with importing a header file from a second header file if the second header wants to expose the first to all clients. For example, header files that declare classes need to be imported directly in subclassing header files, and header files declaring protocols might well be imported directly (although youy can use #protocol ABC; to avoid this).
Go ahead and use #import. The only reason people recommend to use #class when possible is because it makes your code slightly faster to compile. However, there is no issue with #importing one .h file from another. In fact, you need to do this when extending another class.
If you are ok using compiler extensions, you could use this order in Clang:
enum Enum;
typedef enum Enum Enum2;
void f(Enum2); // ok. it sees this type's true name.
enum Enum {
E_1
};
// ok. now its declaration is visible and we can use it.
void f(Enum2 e) {
}
Note: It will trigger a -Wpedantic warning.
If you are using C++11, you should use their enums, which are safe to forward declare -- e.g. enum class Enum:uint8_t; (not a compiler extension).
What worked for a forward declaration of an enum for me in an Objective C .h file was look in the ProjectName-Swift.h file and see what it put, which happened to be the following:
enum SwiftEnumName : NSInteger;
I needed this forward declaration because I had a function parameter type of SwiftEnumName. And it wouldn't let me put the ProjectName-Swift.h import in the Objective C .h file.
Then in the Objective C .m file I just had the #import "ProjectName-Swift.h" in it and just used the SwiftEnum normally.
This was using Swift 4.1.2.
You'd have to either #import them anyway or create a separate header file containing only the typedef. Not importing header files in a header makes the compilation faster, but doesn't change anything else.
Why doesn't C++ support forward declaration of enums?