Accessing ObjC #define BOOL YES/NO from Swift [duplicate] - ios

I have define #define baseUrl [NSString stringWithFormat:#"%#api/v4", MAINURL] in objective c class and can access anywhere in project. But now i have created swift file in exiting objective c project and want to access baseurl in swift but error received.
Use of unresolved identifier 'baseUrl'
how can resolve it?

Importing Objective-C macros into Swift doesn't always work. According to documentation:
Swift automatically imports simple, constant-like macros, declared with the #define directive, as global constants. Macros are imported when they use literals for string, floating-point, or integer values, or use operators like +, -, >, and == between literals or previously defined macros.
C macros that are more complex than simple constant definitions have no counterpart in Swift.
An alternative in your case would be to use a function that returns the value defined by macro
// .h file
#define baseUrl [NSString stringWithFormat:#"%#api/v4", MAINURL]
+ (NSString*) BaseUrl;
// .m file
+ (NSString*) BaseUrl { return baseUrl }

Unfortunately, Objective-C #define could not access by swift.
Maybe you change your #defines to actual constants, which is better than #defines.
Or create a swift object hold some static variable.
For example:
class API: NSObject {
static let apiPrefix = "https://vvv"
static let apiPosts = apiPrefix + "posts"
}
Calling: API.apiPosts

Related

Passing a Swift string to C

I am pretty new to Swift, and I don't have much exposure to C.
I am trying to write a function in C that will get a Swift string that I can then do something with. The problem is that I'm not 100% sure what the type should be in Swift to make C like what it sees.
So far, I have found several examples on Stack that seem like good starting points, but some examples seem dated for the current version of Swift.
I first started by using this example to get C and Swift talking to one another: Swift call C call Swift? I then took that and tried updating the Swift function to return a string of some kind. I understand that it needs to be a UTF-8 return type, but I'm not sure how to go about sending things properly. I've looked at How to pass a Swift string to a c function?, How to convert String to UnsafePointer<UInt8> and length, and How to convert string to unicode(UTF-8) string in Swift?, but none of them really work for a solution. Or I'm just typing it in incorrectly. So far, the closest I can get to returning something is as follows.
In Swift, my ViewController is:
import UIKit
class ViewController: UIViewController {
#_silgen_name("mySwiftFunc") // give the function a C name
public func mySwiftFunc(number: Int) -> [CChar]
{
print("Hello from Swift: \(number)")
let address: String = "hello there";
let newString = address.cString(using: String.Encoding.utf8)
return newString!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
blah()
}
}
And in C, the header is like:
#ifndef cfile_h
#define cfile_h
#include <stdio.h>
const char * mySwiftFunc(int);
int blah(void);
#endif /* cfile_h */
And the source is like:
#include "cfile.h"
int blah() {
const char * retVal = mySwiftFunc(42); // call swift function
printf("Hello from C: %s", retVal);
return 0;
}
There is a bridging header file that just has #include "cfile.h". Obviously, there is still a lot of remnants from the first example, and these will be cleaned up later.
What needs to change to make this work? Right now, the console spits out
Hello from Swift: 42
Hello from C: (B\214
The Swift equivalent of const char * is UnsafePointer<CChar>?, so that's the correct return value. Then you have to think about memory management. One options is do allocate memory for the C string in the Swift function, and leave it to the caller to release the memory eventually:
public func mySwiftFunc(number: Int) -> UnsafePointer<CChar>? {
print("Hello from Swift: \(number)")
let address = "hello there"
let newString = strdup(address)
return UnsafePointer(newString)
}
passes a Swift string to strdup() so that a (temporary) C string representation is created automatically. This C string is then duplicated. The calling C function has to release that memory when it is no longer needed:
int blah() {
const char *retVal = mySwiftFunc(42);
printf("Hello from C: %s\n", retVal);
free((char *)retVal);
return 0;
}
⚠️ BUT: Please note that there are more problems in your code:
mySwiftFunc() is an instance method of a class, and therefore has an implicit self argument, which is ignored by the calling C function. That might work by chance, or cause strange failures.
#_silgen_name should not be used outside of the Swift standard library, see this discusssion in the Swift forum.
A slightly better alternative is #_cdecl but even that is not officially supported. #_cdecl can only be used with global functions.
Generally, calling Swift functions directly from C is not officially supported, see this discussion in the Swift forum for the reasons and possible alternatives.

typedef struct from C to Swift

I'm working on a Swift app that accesses a C library.
The .h file contains a typedef struct:
typedef struct _DATA_KEY_ * CURRENT_DATA_KEY;
And there a method in another class:
-(int) initWithKey(CURRENT_DATA_KEY * key);
I need to create a CURRENT_DATA_KEY in my code. Not sure how I can achieve that in Swift.
There is some old Objective-C code that uses:
CURRENT_DATA_KEY key = NULL;
initWithKey(key)
I've tried:
let myKey = UnsafeMutablePointer<CURRENT_DATA_KEY>.allocate(capacity: 1)
But when I try to use it later as an argument in the function, I get the error:
Cannot convert value of type 'UnsafeMutablePointer<_DATA_KEY_>' (aka 'UnsafeMutablePointer(<OpaquePointer>)') to expected argument type 'UnsafeMutablePointer(<_DATA_KEY_?>!)'
Which looks like the function is expecting an optional value?
I also tried:
let myKey: CURRENT_DATA_KEY? = nil
let myKey: CURRENT_DATA_KEY = NSNull()
But those get similar type errors.
How do I create: UnsafeMutablePointer(<_DATA_KEY_?>!)
It's been a bit since I've done this but IIRC this:
typedef struct _DATA_KEY_ * CURRENT_DATA_KEY;
-(int) initWithKey(CURRENT_DATA_KEY * key);
Is actually equivalent to:
-(int) initWithKey(struct _DATA_KEY_ ** key);
So if we look up Interacting with C APIs: Pointers it falls under:
Type **
Which becomes:
AutoreleasingUnsafeMutablePointer<Type>
If you know the members of the C struct then you may be able to treat it like a Swift struct and use the synthesized init method. So if it is defined like this:
struct _DATA_KEY_ {
int foo
};
It becomes like this in Swift:
public struct _DATA_KEY_ {
var foo: Int
init()
init(foo: Int)
}
And you call the init method like:
// no parameter
let myKey = AutoreleasingUnsafeMutablePointer<_DATA_KEY_>(&_DATA_KEY_())
// parameter
let myKey = AutoreleasingUnsafeMutablePointer<_DATA_KEY_>(&_DATA_KEY_(foo: 12))
I have not tried this code but I've followed the advice in the Apple document before and it's worked out well.

Issue with converting NSString * to String and passing Character as parameter in Swift iOS

I am calling a Objective method from Swift by using a Bridging header.
-(NSString *) PatternSetCreator: (char)Signature detection_time_in_sec:(int)detection_time_in_sec patternLength:(int)patternLength maxPatternSetSize:(int)maxPatternSetSize
There are two issuse:
a) I am not able to pass a single character as parameter while calling this method from Swift
b) I am not exactly sure how to get the return type NSString and assign it to a String variable
A single C char in Swift is represented as CChar, a typealias for Int8.
(similarly, C int in Swift is CInt, a typealias for Int32)
If you want a specific character and are using Swift 1.2, there’s an initializer for UInt8 that takes a UnicodeScalar. Annoyingly, though, you have to then convert it to a Int8 to make it compatible with the C method:
let ch = CChar(UInt8(ascii: "x"))
let i = CInt(100)
let s = obj.PatternSetCreator(ch,
detection_time_in_sec: i,
patternLength: i,
maxPatternSetSize: i)
You should not need to do anything special to turn the returned NSString to a String. The bridging will do that automatically.
(or rather, it’ll return a String! – but if the objective c code is guaranteed to return a valid string every time and never a null pointer, the definition can be changed to -(nonnull NSString *) PatternSetCreator: etc… which means it will return a String instead)

How to bridge Swift String to Objective C NSString?

Am I taking crazy pills? Directly out of the documentation:
“Swift automatically bridges between the String type and the NSString class. This means that anywhere you use an NSString object, you can use a Swift String type instead and gain the benefits of both types—the String type’s interpolation and Swift-designed APIs and the NSString class’s broad functionality. For this reason, you should almost never need to use the NSString class directly in your own code. In fact, when Swift imports Objective-C APIs, it replaces all of the NSString types with String types. When your Objective-C code uses a Swift class, the importer replaces all of the String types with NSString in imported API.
To enable string bridging, just import Foundation.”
I've done this... consider:
import Foundation
var str = "Hello World"
var range = str.rangeOfString("e")
// returns error: String does not contain member named: rangeOfString()
However:
var str = "Hello World" as NSString
var range = str.rangeOfString("e")
// returns correct (2, 1)
Am I missing something?
To go from String to NSString use the following constructor:
let swiftString:String = "I'm a string."
let objCString:NSString = NSString(string:swiftString)
With Xcode 7 (beta), using a downcast from String to NSString, as in below example, will result in a warning message, Cast from 'String?' to unrelated type 'NSString' always fails:
let objcString:NSString = swiftString as! NSString // results in error
You already have the answer in your question. You're missing the cast. When writing Swift code, a statement such as this one
var str = "Hello World"
creates a Swift String, not an NSString. To make it work as an NSString, you should cast it to an NSString using the as operator before using it.
This is different than calling a method written in Objective-C and supplying a String instead of an NSString as a parameter.
Here is example for this :
string str_simple = "HELLO WORLD";
//string to NSString
NSString *stringinObjC = [NSString stringWithCString:str_simple.c_str()
encoding:[NSString defaultCStringEncoding]];
NSLog(stringinObjC);

Equivalent to static fields in IOS

I'm more of an Android developer, but i'm beginning to see the light at the end of the tunnel on iOS development.
There is, however, one coding pattern I can't seem to find an equivalent for.
The use of static fields as flags.
Android :
public final static int ERROR_EMPTY = 1;
public final static int ERROR_NO_CONNECTION = 2;
public final static int ERROR_WRONG_USER = 4;
...
if (error == MyClass.ERROR_EMPTY) {//do things}
What would be the proper way to achieve this on iOS ?
Thanks.
Using Objective-C and C
i often use prefixes:
typedef enum MyClass_Error {
// never use MyClass_Error_Undefined
// or you may favor MyClass_Error_None for a valid error code
MyClass_Error_Undefined = 0,
MyClass_Error_Empty = 1,
MyClass_Error_NoConnection = 2,
MyClass_Error_WrongUser = 4
// ...
} MyClass_Error;
for these value collections. then you get benefits such as typesafety and switch value checking.
for non-type constants:
enum { MyClass_ConstantName = 4 };
and feel free to hide these in the *.m when private.
also note that C enums may have gaps in their defined values (unlike Java's).
Update: there's an even better way to declare an enum, as demonstrated in Abizern's answer -- if you're sticking with the most recent toolchains. the big reason to use this extension is for binary compatibility and encoding (although i favor fixed-width types for these purposes).
There are a few other variations, for the cases when you want to use existing types:
Private Constant
MyClass.m
static const NSRange MyClass_InputRange = {1,1};
Public Constant
MyClass.h
extern const NSRange MyClass_InputRange;
MyClass.m
const NSRange MyClass_InputRange = {1,1};
Using C++
You would likely favor introducing a new scope for these values -- either in a class or a namespace, rather than simulating the scope using prefixes.
Common Mistakes
Use of #define for constants (unless definition is mandatory when preprocessing)
Use of short identifiers, and identifiers which are not prefixed
Use of static values in headers
Not using const when possible
Declaring them in the header, when they could be in the *.m source.
Just to add to Justin's excellent answer - the Modern Objective-C definition for the enum would be:
typedef enum MyClass_Error : NSUInteger {
// never use MyClass_Error_Undefined
// or you may favor MyClass_Error_None for a valid error code
MyClass_Error_Undefined = 0,
MyClass_Error_Empty = 1,
MyClass_Error_NoConnection = 2
// ...
} MyClass_Error;

Resources