Swift - Compiler - other swift flags are not visible to objective c files - ios

my objective c code is not called because custom flag defined in settings is not visible here. Why? How can I define the same for objc?

For Swift it is enough to define Custom Flags as:
-DMYFLAG
for Objective-C you need to define Preprocessor Macros:
MYFLAG=1
Then in both Swift and ObjC files you can use it like:
#if MYFLAG
//code
#endif

Related

Can iOS Objective-C app use nested static ObjC/Swift libs?

OBJ-C ONLY...
That is,
An ObjC app imports ObjC static lib A.
Static lib A imports static lib B.
Static lib A has functions that call functions within lib B.
The app only calls functions in lib A and does not call functions in lib B.
Can I assume that lib A or B can be either Obj-C or Swift?
IE. Can an ObjC app import an ObjC-or-Swift static lib A that itself imports a second ObjC-or-Swift static lib B? (yes, 4 use case permutations)
the git repository https://github.com/CombineCppSwiftObjcInStaticLib i created for you is showing this..
your initial #objc func run_central() in BLE_central.swift is exposed, which triggers the precompiler to generate objc compatible headers (bridge) which then again makes it possible to call the function from a method inside .mm(objc++) or .m(objc) when this generated header is imported.
In fact Hub_lib inside the repo is a static ObjC++ lib mixed with Swift. It would work the other way around also. The headers are the key for success here. If you can provide some objc or c or c++ header to swift functions it becomes compatible and wise versa. I mean in general, thats the idea of headers. If you don't have headers, that does not mean you can not call some external stuff, it just means you would call it blind. A proper IDE will complain before you even try to do this evil stuff, unknown entry points aka unknown symbols etc.. So you go for a proper header - always.
To properly combine swift with other languages its good to know there are always two ways of bridging.
In case of Objective-C (and also Objective-C++) it is
Bridging into Swift (projectname-Bridging-Header.h),
and Bridging out of Swift (expose with #objc to trigger automatically internal generation of projectname-Swift.h file. So this header is "invisible" in the file browser on the left side. Nor will you find it in the repo as file, it is named by modulename which is the project-name). The last mentioned header you could even write manually yourself, with lots of troublesome back-draws.
Hint: Executable code is executable code. No matter what language, as far it is compiled for the right device architecture and has symbols to call and you know what to do with the data returned.
Another Hint: there is a way to handle C pointers in swift see docu which become swift datatypes which you can use to go the other way and declare functions to return those from swift.
And direct use of C in Swift is also possible. The compiler considers if you explicit mark some code as C. extern "C" { /* code */ } will cause the C++ compiler to remember, this is still C++ code to compile the function in such a way, it can be called from C (and Swift)
//Example.hpp //no target membership
#ifdef __cplusplus
#include <stdio.h>
class Example {
private:
const char * _name;
public:
Example(const char *name);
~Example(void);
int getLen(void);
};
#endif
There should be an Example.cpp and don't forget to tell Xcode you deal with c++ #ifdef __cplusplus + #endif
//Example.cpp //has target membership
#include "Example.hpp"
#ifdef __cplusplus
#include <stdio.h>
#include <string>
//code implementation according to example.hpp
Example::Example(const char *name) {
_name = name;
}
int Example::getLen() {
return (int)strlen(_name);
}
#endif
//ExampleWrapper.cpp //has target membership
#include "Example.hpp" //c++ header file
extern "C" int myCppFunction(const char *s)
{
// Create an instance of Example, defined in the library
// and call getLen() on it, return result.
return Example(s).getLen();
}
So this function needs to be declared in the bridging header to make use of it.
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
// for extern "C" functions declare them one by one here
// as implemented in ExampleWrapper.cpp
// becomes "func myCppFunction(_ s: UnsafePointer<Int8>!) -> Int32" in swift
int myCppFunction(const char * s);
and then call from swift..
os_log("The result from C++ is %u", myCppFunction("12345"))
So in fact, yes. Integrating a static lib A that calls static lib B in App is possible. Happy compiling as long you offer some header for each part that needs to know what is inside the head of the other lib. That is true for Apps as it is true for libs and frameworks under each other.
Edit here some important stuff to read about Swift Package Manager C support https://github.com/apple/swift-evolution/blob/master/proposals/0038-swiftpm-c-language-targets.md
As long as the libraries export Objective-C compatible symbols, it doesn't matter if they're written in Objective-C, or Swift, or C++, or any other compiled language.
And we know that the Swift compiler exports Objective-C compatible symbols for all declarations that are marked with #objc (either explicitly or implicitly).
From a consumer perspective it doesn't matter which language generated the libraries, as long as the Objective-C compiler/linker can consume the symbols exported by those libraries.

What is missing after Bridging Obj-C to Swift

I have done this before, normally without trouble, setting the bridging header, etc.
This time, I tried to import a Obj-C framwork (github.com/jensmeder/FSKModem/) into my swift project. When typing code I can "see" all the Obj-C methods.
But when I compile the code, I get errors like "Use of undeclared identifier 'delete'" or "Use of undeclared identifier 'new'". Example of code in a .m file that is giving the error:
_audioFormat = new AudioStreamBasicDescription();
I'm familiar with Swift and don't know about Obj-C but I guess "delete" or "new" should exist, right? Do they belong to some framework that I should add?
I can compile the original code in Obj-C without trouble, but I need to incorporate it in my project that is written in Swift.
Many thanks for any help
Some more information...
The following functions are inside the .m file. Do you know why the "new" and "delete" keywords are unrecognized? This works fine when compiled as a normal "Obj-C" project. The error appears in Swift project only (after bridging headers of course):
-(void)dealloc
{
[self disconnect:NULL];
if (_audioFormat)
{
delete _audioFormat;
}
}
-(void) setupAudioFormat
{
_audioFormat = new AudioStreamBasicDescription();
//...
}
Should I add some #include that I might be missing, besides Foundation?
new and delete are C++ keywords; you can use them in C++ files (usually .cpp or .cc) and in Objective-C++ files (always .mm). You cannot use them in Swift (.swift) or in Objective-C (.m) files.
It is perfectly valid to use an Objective-C class from an Objective-C++ file from Swift; you can use Objective-C to wrap C++ classes for Swift usage. However it is not valid to use C++ from plain Objective-C.
Quite probably you just need to rename your Objective-C file to .mm.
new and delete are keywords from C++ not from Swift or Objective-C.
Swift
let audioFormat = AudioStreamBasicDescription()
Objective-C
AudioStreamBasicDescription* audioFormat = [[AudioStreamBasicDescription alloc] init];
In Swift, you simply need to call the object initializer when creating an object.
_audioFormat = AudioStreamBasicDescription()
The new keyword is an Objective-C combo of alloc and init.

INADDR_LOOPBACK macro from <netinet/in.h> not imported in swift

I'm trying to use the peertalk framework which has no documentation.
On their obj-c example they use the INADDR_LOOPBACK macro, and example is working.
But when i try to do the same in swift the system throw me an unresolved identifier error.
Anyone knows how to fix it?
http://www.gnu.org/software/libc/manual/html_node/Host-Address-Data-Type.html
Update for Swift 3: As of Swift 3, INADDR_LOOPBACK
is imported into Swift. Therefore it suffices to add
#include <netinet/in.h>
to the bridging header file, but a custom definition is not
needed anymore.
Old answer: For some reason, the macro definition
#define INADDR_LOOPBACK (u_int32_t)0x7f000001
from <netinet/in.h> is not imported into Swift.
The problem might be the (u_int32_t) cast, because
other macros like
#define INADDR_NONE 0xffffffff /* -1 return */
are imported.
One solution is to define
let INADDR_LOOPBACK = UInt32(0x7f000001)
in your Swift code. Alternatively, add
#include <netinet/in.h>
const uint32_t kInAddrLoopback = INADDR_LOOPBACK;
to the bridging header file and use kInAddrLoopback in the Swift code.
This is less error-prone because you don't have to repeat the constant.
From the Apple documentation.
Declare simple macros as global constants, and translate complex
macros into functions.

Calling objective c function from SWIFT through bridging header

I have ported an objective-c package into a swift package and included it within the bridging header. Everything works fine, except there is a function I have to implement from a delegate I can't get working:
- (void) mdwamp:(MDWamp*)wamp sessionEstablished:(NSDictionary*)info;
Does anyone know how I can implement this in SWIFT?
In the bridging header make sure you imported the objective-c header.
After doing that, you can use the following.
var instance: ClassName = ClassName()
instance.mdwamp(wamp, sessionEstablished: info)

In absence of preprocessor macros, is there a way to define practical scheme specific flags at project level in Xcode project

Before swift I would define a set of schemes for alpha, beta, and distribution builds. Each of these schemes would have a set of macros that were defined to gate certain behaviors at the project level. The simplest example is the DEBUG=1 macro that is defined by default for all Xcode projects in the default scheme for the Run build. One could query #ifdef DEBUG ... and make decisions in the code accordingly, even compiling out non-necessary code.
It seems that this type of configurational gating is not as easy using swift, as macros are not supported. Can someone suggest a comparable approach, I don't care if the code is compiled out, per se. I would like to gate features based on build scheme, though.
In Swift you can still use the "#if/#else/#endif" preprocessor macros (although more constrained), as per Apple docs. Here's an example:
#if DEBUG
let a = 2
#else
let a = 3
#endif
Now, you must set the "DEBUG" symbol elsewhere, though. Set it in the "Swift Compiler - Custom Flags" section, "Other Swift Flags" line. You add the DEBUG symbol with the -D DEBUG entry.
(Build Settings -> Swift Compiler - Custom Flags)
As usual, you can set a different value when in Debug or when in Release.
I tested it in real code; it doesn't seem to be recognized in a playground.
We ran into an issue with not wanting to set swift compiler flags because we didn't want to have to set them and keep them up to date for different targets etc. Also, in our mixed codebase, we didn't want to make remember to set our flags appropriately all the time for each language.
For ours, we declared a file in ObjC
PreProcessorMacros.h
extern BOOL const DEBUG_BUILD;
In the .m
PreProcessorMacros.m
#ifdef DEBUG
BOOL const DEBUG_BUILD = YES;
#else
BOOL const DEBUG_BUILD = NO;
#endif
Then, in your Objective-C Bridging Header
#import "PreProcessorMacros.h"
Now, use this in your Swift codebase
if DEBUG_BUILD {
println("debug")
} else {
println("release")
}
This is definitely a workaround, but it solved our problem so I posted it here in the hopes that it will help. It is not meant to suggest that the existing answers are invalid.
More swifty solution to Logans method. Set -D DEBUG in Other Swift Flags of Swift Compiler - Custom Flags section in build settings of your target.
Then declare following method in global scope:
#if DEBUG
let isDebugMode = true
#else
let isDebugMode = false
#endif
Now use it as
if isDebugMode {
// Do debug stuff
}
For me, set the debug item of "Active Compilation Condition" to "DEBUG" worked.
Then using DEBGU key work in #IF DEBUG works in debug mode and #ELSE in release mode:
Select your target,
In Build Setting tab search for "Active Compilation Condition",
Set the value of its "Debug" item to "YourKeyWord",
Use simply as follow:
#if DEBUG
print("You'r running in DEBUG mode!")
#else
print("You'r running in RELEASE mode!")
#endif
Swift compiler directives
You can use next compiler directive
#if <some_key>
//logic 1
#else
//logic 2
#endif
//pre Xcode v8
Other Swift Flags(OTHER_SWIFT_FLAGS) = -D <some_key>
-D DEBUG
//from Xcode v8
Active Compilation Conditions(SWIFT_ACTIVE_COMPILATION_CONDITIONS) = <some_key>
DEBUG
I'm working in a mixed language code base where the obj-c code uses a macro to send debug messages to the console (and that macro relies on our debug preprocessor flag). I wanted to be able to call that same macro in the swift code...
I created a class method on one of my obj-c classes that is a wrapper around that macro.
I added that obj-c header to our bridge header file.
Now my swift code calls that class method as a "proxy" to the obj-c macro.
It's mildly annoying that I can't just call the macro straight up in the swift code, but at least now I only have one place in the project to worry about turning my debug flag on/off.

Resources