Using non NS_ENUM objective-C enum in swift - swift

I am using the wahoo fitness API and it defines the following objective-C enum:
typedef enum
{
/** No active connection. */
WF_SENSOR_CONNECTION_STATUS_IDLE,
/** The connection is in process of being established. */
WF_SENSOR_CONNECTION_STATUS_CONNECTING,
/** The sensor connection is established and active. */
WF_SENSOR_CONNECTION_STATUS_CONNECTED,
/** The connection was interrupted (usually occurs when fisica is disconnected). */
WF_SENSOR_CONNECTION_STATUS_INTERRUPTED,
/** The connection is in process of being disconnected. */
WF_SENSOR_CONNECTION_STATUS_DISCONNECTING,
} WFSensorConnectionStatus_t;
I can't find a way to use it in swift. I first tried to do a switch/case on it without success. I am at a point I just want to carry on and tried the following:
var connState : WFSensorConnectionStatus_t = WF_SENSOR_CONNECTION_STATUS_IDLE
...
if( connState == WF_SENSOR_CONNECTION_STATUS_IDLE){
But it does not compile:
'WFSensorConnectionStatus_t' is not convertible to 'NSObject'
Any workaround? I read to use WFSensorConnectionStatus_t.WF_SENSOR_CONNECTION_STATUS_IDLE or WF_SENSOR_CONNECTION_STATUS_IDLE.value but it does not work in xcode beta-4.

The workaround to use .value to get the underlying integer doesn't work anymore as of Beta 4, as you said.
Unfortunately an enum is not transferrable to Swift from Objective-C, it needs to be an NS_ENUM.
I have the same setup as you in a project where I need the enum from an Objective-C framework and use it in Swift.
The workaround I did was to create an Objective-C category that contains an NS_ENUM and there I transfer the values from the framework enum to my own NS_ENUM.
Import the category in your bridging header and you should be able to use the enum as you normally would do.
Something like this:
typedef NS_ENUM(NSUInteger, ConnectionStatus) {
ConnectionStatusIdle
}
- (ConnectionStatus)connectionStatus {
if [self getConnectionStatus] == WF_SENSOR_CONNECTION_STATUS_IDLE {
return ConnectionStatusIdle
}
}
Then you should be able to use it like this:
switch myObject.connectionStatus() {
case .Idle:
// do something
break
}

Here is the final complete solution:
WFSensorConnection+SensorConnectionEnumCategory.h
:
#import <Foundation/Foundation.h>
#import <WFConnector/WFConnector.h>
#interface WFSensorConnection (SensorConnectionEnumCategory)
typedef NS_ENUM(NSUInteger, ConnectionStatus) {
ConnectionStatusIdle,
ConnectionStatusConnecting,
ConnectionStatusConnected,
ConnectionStatusInterrupted,
ConnectionStatusDisconnecting
};
- (ConnectionStatus) swift_connectionStatus;
#end
WFSensorConnection+SensorConnectionEnumCategory.m
:
#import "WFSensorConnection+SensorConnectionEnumCategory.h"
#implementation WFSensorConnection (SensorConnectionEnumCategory)
- (ConnectionStatus) swift_connectionStatus{
if ( [self connectionStatus] == WF_SENSOR_CONNECTION_STATUS_IDLE ){
return ConnectionStatusIdle;
} else if ( [self connectionStatus] == WF_SENSOR_CONNECTION_STATUS_CONNECTING ){
return ConnectionStatusConnecting;
} else if ( [self connectionStatus] == WF_SENSOR_CONNECTION_STATUS_CONNECTED ){
return ConnectionStatusConnected;
} else if ( [self connectionStatus] == WF_SENSOR_CONNECTION_STATUS_DISCONNECTING ){
return ConnectionStatusDisconnecting;
} else if ( [self connectionStatus] == WF_SENSOR_CONNECTION_STATUS_INTERRUPTED ){
return ConnectionStatusInterrupted;
}
return 0;
}
#end
Bridging-Header.h
:
#import "WFSensorConnection+SensorConnectionEnumCategory.h"
Usage:
var sensorConnection: WFSensorConnection?
var connState : ConnectionStatus = ConnectionStatus.Idle
connState = sensorConnection!.swift_connectionStatus()
switch connState {
case ConnectionStatus.Idle:
...
}

C-style enums import in Swift like UInt32. So you can do something like:
let state = unsafeBitCast(WF_SENSOR_CONNECTION_STATUS_IDLE, UInt32.self)
if state == unsafeBitCast(WF_SENSOR_CONNECTION_STATUS_IDLE, UInt32.self) {
//do something
}
Upd: In Swift 2.1 (Xcode 7.1 beta 2) all C-style enums conforms Equatable and you can now use it like:
let state = WF_SENSOR_CONNECTION_STATUS_IDLE
if state == WF_SENSOR_CONNECTION_STATUS_IDLE {
//do something
}
Profit :)

Note that there is a CoreFoundation type that is similar to NS_ENUM called CF_ENUM. I've used it on my framework that is mainly C. And yes, Swift does translate it to a Swift enum.
There is also something similar for NS_OPTIONS called CF_OPTIONS.

Related

Objective-C++ and Swift - nested structs in bridging header

Please help me with the following issue. I am using the swift argument parser library https://github.com/apple/swift-argument-parser
Suppose I have this swift code in some swift file:
#objc public class Math: NSObject, ParsableCommand {
static var configuration = CommandConfiguration(
abstract: "A utility for performing maths.",
subcommands: [Add.self],
defaultSubcommand: Add.self)
}
extension Math {
struct Add: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "Print the sum of the values.")
mutating func run() {
print("0")
}
}
}
` and I want to call to an objective-c++ function named "func" with an argument which is of type Math.Add like so from main.swift file:
var status: Int32 = 0
do {
// try to parse application arguments
var args = try Math.parseAsRoot()
switch args {
case let args as Math.Add:
status = func(args)
default:
try args.run()
}
} catch {
//Some code...
}
What should be the signature of the function and how the bridging-header files should look like? I made the signature be:
extern "C" int func(Math.Add *args);
And my bridging header is like so:
#ifndef __BRIDGING_HEADER_H__
#define __BRIDGING_HEADER_H__
#class Math;
#ifdef __cplusplus
extern "C" {
#endif
int func(Math.Add *args);
#ifdef __cplusplus
}
#endif
#endif /* __BRIDGING_HEADER_H__ */
but is doesn't work and the bridging header doesn't compile (Xcode writes the error: Interface type 'Math' cannot be passed by value; did you forget * in 'Math'?
Objective-C(++) doesn't support nested types, what you can do if you want to keep the nested structure on Swift, is to export a non-nested type for Objective-C:
extension Math {
#objc(Math_Add) // or MathAdd, or whatever name you like
struct Add: ParsableCommand {
, which can then be referenced in your header:
int func(Math_Add *args);
As a side note, I would also change the name of the func function to something that doesn't collide with Swift keywords. Even if you'll be able to call it, it will be confusing for other readers of your code.

React Native Sending Events to JavaScript in Swift

How can I send event's to JavaScript in Swift?
There is examples of Objc code how to send event to JavaScript, but I need to do in swift?
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#implementation CalendarManager
#synthesize bridge = _bridge;
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[#"name"];
[self.bridge.eventDispatcher sendAppEventWithName:#"EventReminder"
body:#{#"name": eventName}];
}
#end
I was just trying to figure this out myself. It was actually surprisingly easy. Heres how I did it:
EventTests.m
#import "RCTBridgeModule.h"
#interface RCT_EXTERN_MODULE(EventTests, NSObject)
RCT_EXTERN_METHOD( testEvent:(NSString *)eventName )
#end
EventTests.Swift
import UIKit
#objc( EventTests )
class EventTests: NSObject {
// Swift doesn't have synthesize - just define the variable
var bridge: RCTBridge!
#objc func testEvent( eventName: String ) {
self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )
}
}
MyModule.js
var React = require( 'react-native' );
var EventTests = require( 'NativeModules' ).EventTests;
var {
Component,
NativeAppEventEmitter
} = React;
var testEventName = 'test';
class MyModule extends Component {
constructor( options ) {
super( options );
// Register for our test event
NativeAppEventEmitter.addListener( testEventName, ( body ) => {
console.log( body );
});
// Call objective c function, which will emit our test event
EventTests.testEvent( testEventName );
}
}
module.exports = MyModule;
Also make sure to include a few imports in your bridging header:
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
Ok, things had already changed, so just to update what I did:
I have my swift module and I need to make it be a RCTEventEmitter
import React
#objc(MyModule)
class MyModule: RCTEventEmitter {
var hasListener: Bool = false
override func startObserving() {
hasListener = true
}
override func stopObserving() {
hasListener = false
}
#objc
func sendMyEvent() {
if hasListener {
self.sendEvent(withName:"MyEvent", body:["name": eventName]];
}
}
#objc
override func supportedEvents() -> [String]! {
return ["MyEvent","MyEvent2"];
}
}
This is how my bridging header looks like:
#import <React/RCTBridgeModule.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
And in the .m file I changed it to:
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface RCT_EXTERN_MODULE(MyModule, RCTEventEmitter)
It is insane. How to pass anything as an event to Javascript, if this event is generated by Swift?
I am stuck with the same issue here: "Subclass RCTEventEmitter instead"
The following does not work anymore.
self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )
I tried:
var emitter: RCTEventEmitter = RCTEventEmitter()
Instead of:
var bridge: RCTBridge!
Conesquently:
self.emitter.sendEvent(withName: eventName, body: "Woot!")
Instead of
self.bridge.eventDispatcher.sendAppEventWithName( eventName, body: "Woot!" )
From bad to worst, I go from a warning to the following hard error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: Location with body: woot. Bridge is not set. This is probably because you've explicitly synthesized the bridge in RCTEventEmitter, even though it's inherited from RCTEventEmitter.'
The documentation is very poor and old. It looks that not many people is using this hot garbage. Very frustrating.
This is an old implementation and it no longer works:
example1
Another article, not very useful:
example2
Very thorough and complex implementation, no one knows if it still works. In my case it doesn't, I get the same error above:
example3
Then there are several objective-c swift cocktails, which I cannot drink at my age:
example4

Translate Objective-C code into Swift

I'm new in Swift and I would like if someone could tell me how can I translate the following code fragment written in Objective-C to Swift.
Code:
id< JavaUtilConcurrentConcurrentNavigableMap > mapVu = mmcache->kvmap_;
The corresponding header:
#interface MemCache : NSObject {
JavaUtilConcurrentConcurrentLinkedDeque *kvqueue_;
}
A bit of confusion later: You cannot access the instance variables of an Objective-C class from within Swift in a regular / proper manner. Take a look at the answer by Leo Natan for how to work around that.
The previous answer below basically focused on the protocol rather than the instance variables
In swift you can just have the protocol as the type:
let mapVu:JavaUtilConcurrentConcurrentNavigableMap = mmcache.kvmap_
See a working example:
protocol JavautilBlaBla {
}
class Class1: JavautilBlaBla {
override init() {
}
}
class Class2 {
var kvmap_:Class1
override init() {
kvmap_ = Class1()
}
}
let mmcache = Class2()
let mapVu:JavautilBlaBla = mmcache.kvmap_
The only current way to access an instance variable is to use valueForKey().
So,
let mapVu = mmcache.valueForKey("kvmap_") as! JavaUtilConcurrentConcurrentNavigableMap!

How to display current class and function running in Swift

I want to return the class and function for debugging purposes. In Objective-C I would use the following, how would I do the same in Swift?
Obective-C
if (debug==1) {
NSLog(#"Running %# '%#'", self.class,NSStringFromSelector(_cmd));
}
Swift
if debug==1 {
println("Running: ", self ,__FUNCTION__)
}
In swift, this would return something like this:
(Running: , <myTeamManager.PlayerInformationTableViewController: 0x7fa18bd31680>, viewDidLoad())
but I only want PlayerInformationTableViewController
reflect(self).summary.pathExtension

Using named value from enum in Objective C

I have an enum defined as follows:
typedef enum modifiers {
modifierNone=-1,
modifierCmd,
modifierShift,
modifierOption
} Modifier;
What i would like to do is pass a string value from one method to another for example (modifierCmd) and create the relevant Modifier to pass to a separate method.
- (void)methodOne:(NSString *)stringValue {
Modifier mod = (Modifier)stringValue;
[self methodTwo:mod];
}
Should this work?
Thanks
Nope. You can use a function, though:
Modifier makeModifier(NSString *s)
{
if ([s isEqualToString:#"modifierNone"]) {
return modifierNone;
} else if ([s isEqualToString:#"modifierCmd"]) {
return modifierCmd;
} /* etc... */
}
- (void)methodOne:(NSString *)stringValue
{
[self methodTwo:makeModifier(stringValue)];
}
I don't think it can work because the data type is really different. Enum is in fact, integer, when NSString is an object. You can use if else to check for modifier. But I recommend to pass the modifier directly.