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

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.

Related

How to bridge Objective-C++ code to Swift when we have C++ classes as parameters in header files?

I'm trying to call C++ methods from Swift, and what I've learnt is that I can wrap the C++ stuff to an Objective-C++ class first, then export this Objective-C++ class to Swift. Here's my code:
// CPPClass.h
class CPPClass {
int data;
};
// OCClass.h
#import "CPPClass.h"
#interface OCClass : NSObject
- (instancetype)initWithCPPObject:(CPPClass &)obj;
#end
// MyProject-Bridging-Header.h
#import "OCClass.h"
When I build the project, the compiler throws an Expected a type error in OCClass.h. If I comment out the header file in MyProject-Bridging-Header.h, everything becomes fine, except that I can't use it in Swift any more.
I can solve it by replacing the specific C++ type parameter with a void * pointer, and in this case I can freely use CPPClass in OCClass.mm file, but I wonder if there's any more elegant solutions?

How to compile .h file which has struct definition with default values [duplicate]

This question already has an answer here:
Force Header Files to Compile as C++ in Xcode
(1 answer)
Closed 2 years ago.
I'm trying to use a 3rd party sample code as part of my Objective-C application.
However, when trying to compile my project I get a lot of compiler errors.
The objc syntax that is used in the sample is quite strange and I believe that it is compiled as something else within the project.
For example, as part of the header file the struct definition has default values:
File.h
struct Options
{
int count = 100;
}
...
In their project, the above header file would compile just fine, but when I try to compile it I see an Xcode error:
Expected ';' at end of declaration list
I'm not an objective-c expert, but from what I've read and also found as part of other posts data types cannot have default values, but somehow it compiles fine in the Xcode sample app.
Please let me know if you need more info or other examples of what would compile fine in their project and doesn't work when I copy and paste the code into mine.
Edit:
Force Header Files to Compile as C++ in Xcode
Individually header files doesn't compiled. Compiled source files where they included. If you include them in .c they will be compiled like C, if in .m - like Objective-c, if in .cpp - like C++, if in .mm - like Objective-C++.
My issue was that I was using this header file into a .m file which compiles it to an Objective-C standard. When I change the extension to .mm everything worked fine.
When you think of objective-C as actually being C its more clear why defaults are not set in .h files
This does not apply to the rules of default sizes which are needed when no flexible memory allocation for a data type is available by default, like for (as is) most data types in C.
Even this two sentences already show the difficulty to distinguish what is meant with "default".
Which is why you end up with definition of initiation processes and functions to declare what exactly is "default" in the other file.
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int count;
} Options;
typedef struct {
int foo;
Options bar[100];
} Foo;
struct Bar {
Foo lotsof;
}
void MyInitiationFunction(Bar *b);
// definition of MyInitiationFunction in .m/.c file
#ifdef __cplusplus
}
#endif
but there is the concept of enum that can trick your mind where defined enumeration default states are what you build some stuff on, which is not a value.

How to call UnitySendMessage method in swift?

I tried calling UnitySendMessage method in my iOS Framework project using swift language but it is showing this error-
Use of unresolved identifier 'UnitySendMessage'
Here is the code snippet for my swift file-
import Foundation
#objc public class Example : NSObject{
#objc open static let shared = Example()
#objc open func printMsg(){
print("\(#function) is called with message:");
UnitySendMessage("CallbackTarget", "OnCallFromSwift", "Hello, Unity!");
}
}
Getting stuck here, can you please tell me what am I missing?
check Answer here.
UnitySendMessage is in libiPhone-lib.a, and as far as I can tell, the
Unity folks don't include any header that declares it.
I just added the following to my code
extern void UnitySendMessage(const char *, const char *, const char
*);
If you look at the generated AppController.mm, they basically do the
same thing for the various UnitySendXXX functions, rather than
including a header.
Having said that, while you will be able to build a library that calls
UnitySendMessage, that library probably won't work unless it's linked
into a Unity project, since UnitySendMessage is going to require more
than just libiPhone-lib.a to actually work.
Just add this code to your Bridging-Header.h file:
#import "UnityInterface.h"
Make sure that your files are in the XCode project that was build from Unity, so the actual UnityInterface file that is created by unity is present.
Worked for me :)

Finding frameworks being linked to a project in Xcode

I saw this question: How can I programatically get the list of frameworks and libraries included in an ios project? here, which tries to answer similar question. However, I've two questions on this
The answer in above link (or see code below) - does it provide all frameworks or "only" frameworks that are linked to the project.
for (NSBundle *framework in [NSBundle allFrameworks])
NSLog(#"%#",framework.bundlePath.lastPathComponent);
if I see a framework appearing in above code, how can I find its usage in my code. I see many frameworks being referred to in above code, but I'm not able to figure out where exactly are they used. As per my knowledge, few of them are not used - is there a proper way to find this out.
Update 1:
I made a simple new app with absolutely no code in it. Then, I executed the for loop above and found that, it also presented me with all the frameworks - which means, the code above simply prints all the frameworks and not the one that I am essentially be using in my app. But, does it mean that all frameworks that are printed are linked to the app?
The dynamic loader dyld(3) provides this information. This code will print all loaded frameworks and shared libraries as well:
#include <stdio.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
int main() {
uint32_t c = _dyld_image_count();
for(uint32_t i = 0; i < c; ++i) {
printf("%d: %s\n", i, _dyld_get_image_name(i));
}
return 0;
}
EDIT: allFrameworks lists only frameworks which are linked to your app, and which contain at least one Objective-C class (see https://developer.apple.com/documentation/foundation/nsbundle/1408056-allframeworks).
Searching for referrers is should be very difficult in general. If you are just looking for a single function, you can add a static implementation of the function and call the loaded variant from it. This technique is used for overwriting __cxa_throw for instance, but it should also work for other functions:
static cxa_throw_t sCxa_throw = 0;
extern "C" void __cxa_throw(void *inException, void *inPvtinfo, void (*inDestination)(void *)) {
if (sCxa_throw == NULL) {
sCxa_throw = (cxa_throw_t)dlsym(RTLD_NEXT, "__cxa_throw");
}
sCxa_throw(inException, inPvtinfo, inDestination);
}
The variable sCXA_throw contains the reference to the dynamic version of this function while the loader uses the static version. Inside of this function you can determine the callers with unwinding the stack with libunwind.

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.

Resources