Using an Objective C enum in Swift back in Objective C - objective-c

I have an enum defined in Objective C and used as a datatype in a Swift object.
Now I'm using that swift object in Objective C and I'd like to access the property.
However the property isn't added to the "xxxx-Swift.h" file in the class. I know it's possible to use Swift enums in Obj C with #objc prepending it but I don't have much control over this enum since it's auto generated from existing Objective C code.

I'm not sure exactly what your use case is, but here is a quick example of how an enum defined in Objective-C can be used in a Swift class, which can in turn be used in Objective-C.
Here is the Objective-C header (oclib.h):
#ifndef oclib_h
#define oclib_h
#import <Foundation/Foundation.h>
typedef enum {A, B, C} oc_enum_t;
void useSwiftClassInC();
#endif /* oclib_h */
Here is the corresponding Objective-C .m file (oclib.m):
#import "oclib.h"
#import "swift_c_1-Swift.h" // Need this to have access to Swift types; the swift_c_1 part will be your product module name.
void useSwiftClassInC() {
UseEnum *ue = [[UseEnum alloc] initWithE:B i:444];
printf("In Objective-C useSwiftClassInC(): m_Enum = %d, m_Int = %d\n", ue.m_Enum, ue.m_Int);
ue.m_Enum = A;
ue.m_Int = 10;
[UseEnum printUseEnum: ue];
}
And here is the Swift file:
// Swift class that uses an enum from Objective-C
class UseEnum : NSObject // NSObject inheritance is important!
{
var m_Enum : oc_enum_t
var m_Int : Int32
init(e : oc_enum_t, i : Int32)
{
m_Enum = e
m_Int = i
}
static func printUseEnum( x : UseEnum )
{
print("In Swift UseEnum::printUserEnum: m_Enum = \(x.m_Enum), m_Int = \(x.m_Int)")
}
}
// This calls an Objective-C function that does the following:
// - creates a UseEnum object
// - prints it out from Objective-C
// - modifies it
// - calls printUseEnum(), implemented in Swift, to print the object
useSwiftClassInC()
The bridging header just has
#import "oclib.h"
You have probably seen this documentation already, but if not, here it is: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Please provide more details about your specific situation if this doesn't answer your question. Here is the output I get from the example:
In Objective-C useSwiftClassInC(): m_Enum = 1, m_Int = 444
In Swift UseEnum::printUserEnum: m_Enum = oc_enum_t(rawValue: 0), m_Int = 10
An interesting situation arises if a data type defined in Swift is used in the signature of an Objective-C function that needs to be called from Swift. Xcode won't let us import the *-Swift.h into an Objective-C header or into a bridging header. A solution is to use #class forward declaration of the Swift type. Thus, if in the above example we needed an Objective-C function that takes UseEnum as a parameter, we could have something like this in oclib.h:
...
#class UseEnum; // Forward declaration must come before UseEnum is used!
...
void useSwiftClassInC_1(UseEnum * useEnum);
...

Related

How to call a Swift function in Rust?

I have a function written in Swift that I want to call from Rust. I've tried exposing it through Objective-C, however I continue to get errors from ld saying it can't find _foo. The Rust project is linked to the Swift project by compiling the Rust project as a staticlib.
foo.h
#pragma once
#include <stdint.h>
uint8_t foo_bridge(uint8_t);
foo.m
#import <Foundation/Foundation.h>
#import <Foo-Swift.h>
uint8_t foo_bridge(uint8_t byte) {
return foo(byte);
}
bar.swift
public func foo(byte: UInt8) -> UInt8 {
return byte * 2
}
src/lib.rs
extern "C" {
pub fn foo_bridge(byte: u8) -> u8;
}
Bar-Bridging-Header.h
#import "foo.h"
Cargo.toml
[package]
name = "foo"
version = "0.1.0"
[lib]
name = "foo"
crate-type = ["staticlib"]
The issue here is attempting to call a bare Swift function from Objective-C, which isn't supported.
If you check the Foo-Swift.h header you'll find that the foo() function isn't present, which indicates that the symbol for it, _foo, won't be available at runtime either.
The solution is to put the foo() function inside something that can be called from Objective-C, like a class deriving from NSObject:
class Foo: NSObject {
class func foo(byte: UInt8) -> UInt8 {
return byte * 2
}
}
Once you do that, you'll find the Foo-Swift.h header now has an interface for it:
#interface Foo : NSObject
+ (uint8_t)fooWithByte:(uint8_t)byte;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Since we know it's available from Objective-C now, we can call it like this:
uint8_t foo_bridge(uint8_t byte) {
return [Foo fooWithByte:byte];
}

How do I use a C++ static library in Obj-C?

I have a c++ static libray: .a file and staticLibrary.h file.
In the .h file, there is a class I want to access:
typedef enum
{
eStaticLibOperationUnknown = 0
eStaticLibOperationSystemCheck = 1
} enumStaticLibOperation;
typedef enum
{
eStaticLibResultUnknown = 0,
eStaticLibResultNullParameter = 4,
eStaticLibResultWrongParameter = 5
} enumStaticLibResult;
typedef std::function<void(void)> typeCallBack;
class classResultHelper
{
blah blah
};
class staticLibrary
{
public:
staticLibrary(typeCallBack, const char*);
void requestOperation(const char*, size_t);
void requestOperation(enumStaticLibOperation, const char*, size_t);
enumStaticLibResult getResult(char**, size_t*);
};
I used #import "staticLibrary.h" at the top of my viewController.m file. This raised an error as it recognized the C++ to be foreign. I then changed the viewController to a .mm extension, making the file Objective-C++ and removing the error.
But when I try to run staticLibrary* sL = [[staticLibrary alloc] init]; in viewDidLoad, I get an error at the second staticLibrary on the right side. It says "receiver type is not an objective-c class". What am I doing wrong?
When looking at the documentation for using the static library it says:
1.1. new staticLibrary(callback, “en”);
1.2. requestOperation(“enumSystemcheck”, NULL, 0);
1.3. callback();
1.4. getResult(... , ...);"
I believe this is Java (?), and the first line is to make an instance of staticLibrary with those parameters. How would I do that in objective-C?
The code you have in the example isn't Java, it's C++. ObjC++ means that you can mix statements made in C++ or ObjC on a line, it doesn't mean you can use C++ objects as if they were ObjC objects, or with the ObjC syntax.
What people usually do is only include C++ headers and write C++ code in the .mm file itself, and not put any in the header. Write an ObjC class that wraps the part of your C++ library that you want. So in your example, that'd be something like:
JCRStaticLibrary.h
#interface JCRStaticLibrary : NSObject
-(instancetype) initWithCallback: (void(^)(void))inObjCCallback;
-(void) requestOperation: (NSString*)what withBuffer: (void*)buf size: (int)theSize;
#end
JCRStaticLibrary.mm
#import "JCRStaticLibrary.h"
#include "staticLibrary.h"
#interface JCRStaticLibrary ()
{
staticLibrary *_cppStaticLibrary;
}
#end
#implementation JCRStaticLibrary
-(instancetype) initWithCallback: (void(^)(void))inObjCCallback
{
self = [super init];
if( self )
{
_staticLibrary = new staticLibrary( inObjCCallback, "en"); // ObjC++ knows how to turn an ObjC block into a C++ lambda.
// Under the hood it generates code like:
// _staticLibrary = new staticLibrary( [inObjCCallback](){ inObjCCallback(); }, "en");
// Where the [inObjCCallback](){} part is how C++ does lambdas, its equivalent to blocks.
// I.e. it's roughly analogous to ObjC's ^(){}.
}
}
-(void) dealloc
{
delete _staticLibrary;
}
-(void) requestOperation: (NSString*)what withBuffer: (void*)buf size: (int)theSize
{
_staticLibrary->requestOperation( [what UTF8String], buf, theSize );
}
#end
Something like that. I don't know what the parameters to requestOperation in your case actually are, so I just made an educated guess.
Instead of passing in the ObjC block to -initWithCallback:, you could also write your own lambda and make it a method, i.e.:
-(instancetype) init
{
__weak JCRStaticLibrary *weakSelf = self; // Avoid retain circle.
_staticLibrary = new staticLibrary( [weakSelf](){ [weakSelf doCallbackThing]; }, "en"); // ObjC++ knows how to turn an ObjC block into a C++ lambda.
}
-(void) doCallbackThing
{
// Do whatever the callback should do here.
}
It really depends on whether the callback changes every time you create an object of this type (e.g. if a JCRStaticLibrary object represents a command sent over the network) or comes from a small set of commands used over and over again (e.g. you receive a video frame and apply a filter to it, then hand it off to another object, so you really only ever have one callback). In the former case you wanna keep the block, in the latter, having a subclass for each filter might make more sense (unless you want to switch between filters on-the-fly).

Swift globals and global functions in objective c

the documentation says:
Global constants defined in C and Objective-C source files are automatically imported by the Swift compiler as Swift global constants.
But it doesn't say anything about the other way around. I need to define a global swift constant and be able to see it one the objective c side like a global c constant. Like on the swift side define:
public let CARDS = ["card1", "card2"]
and see use it on the objective c side like
NSLog(#"Cards count: %d", [CARDS count])
What should I do? I've already imported the swift automatically generated header like:
#import "MyProject-Swift.h"
and in Xcode if I command-click on it, it takes me to the correct place in the swift code, but at compile time I get:
'User of undeclared Identifier CARDS'
on my objective c side.
Here is the document about it
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Global variables (including constants) are unaccessible from Objective-C.
Instead, you have to declare a class which has accessors for the global constants.
// Swift
public let CARDS = ["card1", "card2"]
#objc class AppConstant {
private init() {}
class func cards() -> [String] { return CARDS }
}
// Objective-C
NSArray *cards = [AppConstant cards];
Nice answer by #rintaro, but another alternative simple Swift answer for constants that can be used in both Swift and Objective-C:
#objcMembers
class MyConstants: NSObject {
static let kMyConstant1 = "ConstantValue1";
static let kMyConstant2 = "ConstantValue2";
static let CARDS = ["card1", "card2"]
}
You can access this on both Swift and Objective-C by:
MyConstants.kMyConstant1 // this will return "ConstantValue1"
MyConstants.CARDS // this will return array ["card1", "card2"]
Swift global functions (ie. swift top-level functions) cannot be accessed by objC. Period. End of story.
See above answer from rintaro, to wit...
"This excludes Swift-only features such as those listed here:
. . .
Top-level functions defined in Swift"
boom

How to declare a constant in swift that can be used in objective c

if I declare the swift constant as a global constant like:
let a = "123"
but the a cannot be found in objective c.
How to solve this?
From Apple Doc:
You’ll have access to anything within a class or protocol that’s marked with the #objc attribute as long as it’s compatible with Objective-C. This excludes Swift-only features such as those listed here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Therefore its not possible to access global variables(Constants) or global functions defined in Swift.
Possible Solutions:
From the Apple Document Swift programming language, You can Declare Type Properties as
class var constant: Int = {
return 10
}()
But currently in Swift(beta-3) Type properties are not supported.
You can declare a Class function to get a constant value:
In Swift:
class func myConst() -> String {
return "Your constant"
}
Accessing from Objective-C:
NSString *constantValue = [ClassName myConst];
NSLog(#"%#", constantValue);
Swift code:
public class MyClass: NSObject {
public static let myConst = "aConst"
}
and then in Objective-C:
[MyClass myConst]
Isn't this working as well? As in this works for me.
Also this is somewhat shorter as creating a object first (alloc, init). Making a new function for every constant is... not pretty :/
Update for Swift 4
Because of the changes in Swift 4's Objective-C inference, you need to add the #objc annotation to the declared constant as well. The previous declaration then becomes:
#objcMembers
public class MyClass: NSObject {
public static let myConst = "aConst"
}
The calling Objective-C code remains the same.
Using #objcMembers makes all constants available (as if you'd write #objc before each constant), but I've had times where the compiler somehow wouldn't generate the corresponding ObjC code.
In those cases I'd suggest adding the #objc decorator before the constant as well.
I.e.: #objc public static let myConst = "aConst"
You should not have any problem by using let in Objective-C, next example was made with Xcode 7.2 :
MyClass.swift
import Foundation
import UIKit
#objc class MyClass : NSObject { // <== #objc AND NSObject ARE BOTH NECESSARY!!!
let my_color = UIColor( red:128/255,green:32/255,blue:64/255,alpha:1 ) // <== CONSTANT!!!
}
MyObjectiveC.m
#import "PROJECTNAME-Swift.h" // <== NECESSARY TO RECOGNIZE SWIFT CLASSES!!!
#interface MyObjectiveC ()
#end
#implementation MyObjectiveC
#synthesize tableview; // <== ANY UI OBJECT, JUST AS EXAMPLE!!!
- (void) viewDidLoad () {
MyClass * mc = [ [ MyClass alloc ] init ]; // <== INSTANTIATE SWIFT CLASS!!!
tableview.backgroundColor = mc.my_color; // <== USE THE CONSTANT!!!
}
#end
PROJECTNAME is the name of your Xcode project, as shown in Project Navigator.
In your swift class,
let constant: Float = -1
class YourClass: NSObject {
class func getMyConstant() -> Float {return constant}
...
}
Clean, build to let xcode prepare this method useable for obj-c.
Then at your obj-c class
if ([YourClass getMyConstant] != 0) {
...
}
First of all you need to know about the important of auto-generated Swift header file.
It is the one that will made the magic to transcribe the Swift code to be understandable from Objective-C.
This file is auto-generated by Xcode (do not look for it in your project).
The important of this file is to use the correct name, it can match with your target name, but, may not, it is the product module name. (Search for it in your project settings as "Product module")
You need to import this file on the Objective-C class that you want to use a Swift class and also the Swift class name of your Swift file.
#import <ProductModuleName-Swift.h>
#class MySwiftClassName;
My Swift class should have the prefix #objc and inherit from NSObject:
#objc class MySwiftClassName: NSObject {
let mySwiftVar = "123"
}
Then you can call your Swift variable from the Objective-C file:
MySwiftClassName *mySwiftClassO = [[MySwiftClassName alloc] init];
NSString *myVar = mySwiftClassO.mySwiftVar;
Make sure to clean and rebuild your project after each change to force regenerate this auto-generated file.
If your Swift header file was auto-generated correctly you can navigate to it by clicking over the import file name and check if all the code you need was properly transcribed.
In the following post you can find more detailed information about this. https://solidgeargroup.com/bridging-swift-objective-c
Classes func don't work. The only solution I have found out is this one:
class YourController: NSObject {
#objc static let shared = YourController()
private override init() { }
#objc class func sharedInstance() -> YourController {
return YourController.shared
}
#objc let terms = "Your-String-here"
And then on Obj-c file:
[[YourController sharedInstance].terms]

Objective-C - define an enum than can be dot called like ENUMTYPE.ENUMVAL

I've read many things about enum types in objective-c, and I see there is many ways to define them. But I don't see the right way (if there is one) to define an enum that can be called with CARS.ROLLSROYCE and that cannot be used only with ROLLSROYCE in the code.
So I can define ROLLSROYCE in the CARS enum and also in the BEAUTIFULCARS enum.
Do you know the way to define such an enum ?
You are trying to implement namespaces for your Enums in Objective-C. What you're asking for is a lot of elbow grease in Objective-C. You are probably best-off using C++ for this, since it is easy and afaik fully supported in any iOS or Cocoa application. You'll have to rename the files that #import your C++ code to .mm files instead of .m files, and the C++ compiler can be trickier than the Objective-C one. Going this route you'll create a header file like Enums.h.
// Enums.h
namespace CARS
{
enum CARS
{
ROLLSROYCE
};
}
namespace BEAUTIFULCARS
{
enum BEAUTIFULCARS
{
ROLLSROYCE = 45
};
}
And in your .mm sourcefile
#import "Enums.h"
-(void)printEnumvals
{
NSLog(#"CARS %d BEAUTIFULCARS %d",
CARS::ROLLSROYCE,
BEAUTIFULCARS::ROLLSROYCE);
}
If you want to avoid using C++ for this solution, there's a lot more elbow grease, bookkeeping, and opportunity for error. You'll need a header and a source file for this.
// CARS.h
#interface BEAUTIFULCARS : NSObject
{
enum
{
BEAUTIFULCARS_ROLLSROYCE = 45
} BEAUTIFULCARS;
}
#end
#interface CARS : NSObject
{
enum
{
CARS_ROLLSROYCE
} CARS;
}
#end
// CARS.m
#implementation BEAUTIFULCARS
+(NSInteger)ROLLSROYCE{ return BEAUTIFULCARS_ROLLSROYCE; }
#end
#implementation CARS
+(NSInteger)ROLLSROYCE{ return CARS_ROLLSROYCE; }
#end
Your .m source is almost the same:
#import "CARS.h"
-(void)printEnumvals
{
NSLog(#"CARS %d BEAUTIFULCARS %d",
CARS.ROLLSROYCE,
BEAUTIFULCARS.ROLLSROYCE);
}
Objective-C does not manage scope in the same way that most other OO languages do. Interfaces define the properties and messages that an object that interface supports, but don't support protection levels like public or private. When you define an enum in an #interface, that enum ends up in global scope.
For my case, I didn't want to use C++ namespaces or write redundant Objective-C classes for such a simple case, so I fallen back to the C.
// Enum.h
typedef struct
{
const int ROLLSROYCE;
} _CARS;
typedef struct
{
const int ROLLSROYCE;
} _BEAUTIFULCARS;
extern const _CARS CARS;
extern const _BEAUTIFULCARS BEAUTIFULCARS;
And then in Enum.m, define values
// Enum.m
#import "Enum.h"
const _CARS CARS = {0};// 0 is to be assigned to ROLLSROYCE field in struct
const _BEAUTIFULCARS BEAUTIFULCARS = {1}; // same but with 1
And finally, in your "main" code
#import "Enum.h"
// Some method
{
NSLog(#"I can refer to CARS.ROLLSROYCE = %d and BEAUTIFULCARS.ROLLSROYCE = %d", CARS.ROLLSROYCE, BEAUTIFULCARS.ROLLSROYCE);
}
Which will produce this output:
I can refer to CARS.ROLLSROYCE = 0 and BEAUTIFULCARS.ROLLSROYCE = 1