Swift pass block from Objective-C to Swift - objective-c

I have a method in Objective-C that takes a parameter and returns a result in a block. How do I return this result through a bridging header to a swift block? Note I have other non-block functions returning and this works fine.
Objective-C
- (void)performMethod:(NSString*)myString
{
[self.class
doSomething:myString
onSuccess:^(NSArray * results) {
// Return results
}
onFailure:^(NSError * error) {
// Return error
}];
}
Bridging header
?
Swift
class.performMethod(myString:String)->(results) in NSArray {
}

the trouble is that your Objective C function return void, not some block
how to deal with block in swift ? see the example below
// this block takes an Int as parameter and returns an Int
let myBlock: (Int)->Int = { i in
return i
}
let i = myBlock(10)
print(i) // 10
// this block takes no parameters and return an empty Array<Int>
let myBlock2: ()->Array<Int> = { Void in
return [Int]()
}
print(myBlock2.dynamicType) // () -> Array<Int>
let arr = myBlock2()
print(arr, arr.dynamicType) // [] Array<Int>
from what i can see in your code, you try to do something like
import Foundation
func bar(str: String, onSucces: ()-> NSArray, onError: ()->NSError) {
// do something with str
if str == "alfa" {
// success
let arr = onSucces()
print(arr)
} else {
let err = onError()
print(err)
}
}
func foo(str: String) -> Void {
bar(str, onSucces: { () -> NSArray in
return NSArray(array: [str])
}) { () -> NSError in
return NSError(domain: "error", code: -1, userInfo: nil)
}
}
foo("alfa")
/*
(
alfa
)
*/
foo("")
/*
Error Domain=error Code=-1 "(null)"
*/

Related

How to return a Hash/Raku object from native call?

I am writing a library that uses NativeCall, it would be very convenient for me to be able to return a Raku Hash from an exported function. How can I do this?
For example, in Ruby, if I wanted to return a Hash from C, I would do something like:
#include "ruby.h"
VALUE make_hash() {
VALUE hash = rb_hash_new();
return hash;
}
I am interested to see if this can be done, I was thinking that maybe I would need to use a MoarVM header or something. But I'm not sure.
What I'm trying to do is write a C function that takes in a String does some stuff, then returns a Raku hash.
it would be very convenient for me to be able to return a Raku Hash from an exported function
A workaround could be to let the C function return a struct with key and values and then write a Raku wrapper that converts that into a Raku hash like this:
use v6;
use NativeCall;
constant LIB = ('./libmylib.so');
class HInfo is repr('CStruct') is export {
has Str $.key1;
has num64 $.value1;
has Str $.key2;
has num64 $.value2;
}
sub foo_(Str--> HInfo) is native(LIB) is symbol('foo') { * }
sub foo(Str $str --> Hash) {
my HInfo $hinfo = foo_($str);
my %h;
%h{$hinfo.key1} = $hinfo.value1;
%h{$hinfo.key2} = $hinfo.value2;
return %h;
}
my %h = foo("bar");
dd %h;
I have done roughly this for Rust over here (this is a collection of some Raku-Rust Nativecall code examples, not a module)...
First the raku:
## Rust FFI Omnibus: Objects
## http:##jakegoulding.com/rust-ffi-omnibus/objects/
class ZipCodeDatabase is repr('CPointer') {
sub zip_code_database_new() returns ZipCodeDatabase is native($n-path) { * }
sub zip_code_database_free(ZipCodeDatabase) is native($n-path) { * }
sub zip_code_database_populate(ZipCodeDatabase) is native($n-path) { * }
sub zip_code_database_population_of(ZipCodeDatabase, Str is encoded('utf8'))
returns uint32 is native($n-path) { * }
method new {
zip_code_database_new
}
submethod DESTROY { # Free data when the object is garbage collected.
zip_code_database_free(self);
}
method populate {
zip_code_database_populate(self)
}
method population_of( Str \zip ) {
zip_code_database_population_of(self, zip);
}
}
my \database = ZipCodeDatabase.new;
database.populate;
my \pop1 = database.population_of('90210');
my \pop2 = database.population_of('20500');
say pop1 - pop2;
Then the Rust:
// Rust FFI Omnibus: Objects
// http://jakegoulding.com/rust-ffi-omnibus/objects/
pub struct ZipCodeDatabase {
population: HashMap<String, u32>,
}
impl ZipCodeDatabase {
fn new() -> ZipCodeDatabase {
ZipCodeDatabase {
population: HashMap::new(),
}
}
fn populate(&mut self) {
for i in 0..100_000 {
let zip = format!("{:05}", i);
self.population.insert(zip, i);
}
}
fn population_of(&self, zip: &str) -> u32 {
self.population.get(zip).cloned().unwrap_or(0)
}
}
#[no_mangle]
pub extern "C" fn zip_code_database_new() -> *mut ZipCodeDatabase {
Box::into_raw(Box::new(ZipCodeDatabase::new()))
}
#[no_mangle]
pub extern "C" fn zip_code_database_free(ptr: *mut ZipCodeDatabase) {
if ptr.is_null() {
return;
}
unsafe {
Box::from_raw(ptr);
}
}
#[no_mangle]
pub extern "C" fn zip_code_database_populate(ptr: *mut ZipCodeDatabase) {
let database = unsafe {
assert!(!ptr.is_null());
&mut *ptr
};
database.populate();
}
#[no_mangle]
pub extern "C" fn zip_code_database_population_of(
ptr: *const ZipCodeDatabase,
zip: *const c_char,
) -> u32 {
let database = unsafe {
assert!(!ptr.is_null());
&*ptr
};
let zip = unsafe {
assert!(!zip.is_null());
CStr::from_ptr(zip)
};
let zip_str = zip.to_str().unwrap();
database.population_of(zip_str)
}
Obviously the C side of affairs will need to be quite different, but hopefully this gives enough clues.
As someone suggested, this is best done with a wrapper function. First things first though, what kind of value are you returning from C?
Your best bet is to return a CStruct.

How do we return an optional enum value from Swift to Obj-C?

We know how to expose Swift enums to Obj-C:
#objc enum Animal: Int {
case Cat, Dog
}
But the compiler complains that the following "cannot be represented in Obj-C":
func myAnimal() -> Animal? {
if hasPet() {
return .Cat
} else {
return nil
}
}
Ideas?
Optional Ints in Swift don't map to a type in Objective-C. The issue is that your myAnimal() method is returning a type that can't be represented in Objective-C.
The way I see it, you have two options...
Option1: Change your method's return type:
func myAnimal() -> NSNumber? {
// method body goes here.
}
This doesn't really seem great since in Objective-C you'd have to do something like this:
if (myAnimal().integerValue == AnimalCat) {
// do stuff here
}
Option 2: Add a catch-all case in your enum
#objc enum Animal: Int {
case none = 0
case cat = 1
case dog = 2
init(rawValue: Int) {
switch rawValue {
case 1: self = Cat
case 2: self = Dog
default: self = None
}
}
}
// Updated method signature that makes the compiler happy.
func myAnimal() -> Animal {
if hasPet() {
return .Cat
} else {
return .None
}
}
This way, you can change your method signature to not return an optional, and the compiler will be happy.

Swift: write a function that allocates an instance of (any) class

I'm looking for the equivalent of the following Obj-C code in Swift:
- newInstanceOf:(id)classRef {
return [classRef new];
}
and then to use it:
id instance = [whatever newInstanceOf:NSArray.class]
[instance isKindOfClass:NSArray.class] == YES
I have tried using a Swift template:
func newSomething<T>(classRef:T.Type) -> T {
return classRef()
}
I get the error: error: 'T' cannot be constructed because it has no accessible initializers
You could create a protocol to act as a type constraint for objects initializable by a void-argument initializer, and thereafter extend your types of choice to this protocol.
protocol SimplyInitializable {
init()
}
extension Int : SimplyInitializable { }
extension Double : SimplyInitializable { }
extension String : SimplyInitializable { }
struct MyStruct {
var myInt : Int
init() {
myInt = 0
}
}
extension MyStruct : SimplyInitializable { }
func newSomething<T: SimplyInitializable>(classRef: T.Type) -> T {
return classRef.init()
}
/* Examples */
var a = newSomething(Int)
var b = newSomething(Double)
var c = newSomething("".dynamicType)
var d = newSomething(MyStruct)
var e = newSomething(a.dynamicType)
print(a.dynamicType) // Int
print(b.dynamicType) // Double
print(c.dynamicType) // String
print(d.dynamicType) // MyStruct
print(e.dynamicType) // Int
Actually in Swift, not all classes are guaranteed to have an init() initializer. It can work with NSObject classes though because NSObject does have that requirement.
func newInstanceOf<T:NSObject>(aClass:T.Type) -> NSObject
{ return aClass.init() }
let string = newInstanceOf(NSString) // ""
let date = newInstanceOf(NSDate) // 2016-01-15 05:27:29 +0000

Alamofire 3.0 and swift 2.0 broke my APIWrapper

I recently updated to swift 2.0 and it destroyed my alamofire serializer and API wrapper.
extension Alamofire.Request {
class func authResponseSerializer() -> Serializer {
return {request, response, data in
if data == nil {
return (nil, nil)
}
var jsonError: NSError?
let jsonData:AnyObject?
do {
jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
} catch var error as NSError {
jsonError = error
jsonData = nil
} catch {
fatalError()
}
if jsonData == nil || jsonError != nil {
return (nil, jsonError)
}
let json = JSON(jsonData!)
if json.error != nil || json == nil {
return (nil, json.error)
}
var auth:Authorization = Authorization(json: json)
return (auth, nil)
}
}
func responseAuthorization(completionHandler: (NSURLRequest, NSHTTPURLResponse?, Result<Authorization>) -> Void) -> Self {
return response(responseSerializer: Request.authResponseSerializer(), completionHandler: completionHandler)
}
in the request extension i get the error
and in the second func responseAuthoriztion
Use of undeclared type 'Serializer'
I get the error generic type Result specialized with too few type parameters (got 1, but expected 2)
How do i migrate my code to alamofire 3.0 and swift 2.0? I've been reading the migration guides and can't figure this out, many other people I'm sure are dealing with this same problem.

Custom equality in swift objects preserving compatibility with legacy Objective-C code

In Objective-C you would do something along the lines of
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self.customProperty isEqual:other.customProperty];
}
My first naive attempt in swift goes as follows
func isEqual(other: AnyObject) -> Boolean {
if self === other {
return true
}
if let otherTyped = other as? MyType {
return self.myProperty == otherTyper.myProperty
}
return false
}
But I'm far from being happy with it. I don't even know whether the signature is right or whether we're supposed to use anything different than isEqual.
Any thoughts?
EDIT:
I'd also like to keep Objective-C compatibility (my class is used in both legacy Obj-C code and new Swift code). So I think only overriding == isn't enough. Am I wrong?
Yes, you need to override isEqual (and hash) to make your objects fully Objective-C compatible. Here's a Playground-ready example for the syntax:
import Foundation
class MyClass: NSObject {
var value = 5
override func isEqual(object: AnyObject?) -> Bool {
if let object = object as? MyClass {
return value == object.value
} else {
return false
}
}
override var hash: Int {
return value.hashValue
}
}
var x = MyClass()
var y = MyClass()
var set = NSMutableSet()
x.value = 10
y.value = 10
set.addObject(x)
x.isEqual(y) // true
set.containsObject(y) // true
(syntax current as of Xcode 6.3)
You could also implement a custom equatable, for instance:
func == (lhs: CustomClass, rhs: CustomClass) -> Bool {
return lhs.variable == rhs.variable
}
This will allow you to simply check equality like this:
let c1: CustomClass = CustomClass(5)
let c2: CustomClass = CustomClass(5)
if c1 == c2 {
// do whatever
}
Be sure your custom equatable is outside the class scope!
swift3 sig:
open override func isEqual(_ object: Any?) -> Bool {
guard let site = object as? PZSite else {
return false
}
....
}
In Swift you can override infix operators (and even make your own). See here.
So rather than using isEqual you could do:
myType == anotherType
One more example
public class PRSize: NSObject {
public var width: Int
public var height: Int
public init(width: Int, height: Int) {
self.width = width
self.height = height
}
static func == (lhs: PRSize, rhs: PRSize) -> Bool {
return lhs.width == rhs.width && lhs.height == rhs.height
}
override public func isEqual(_ object: Any?) -> Bool {
if let other = object as? PRSize {
if self === other {
return true
} else {
return self.width == other.width && self.height == other.height
}
}
return false
}
override public var hash : Int {
return "\(width)x\(height)".hashValue
}
}
To archive Objective-C compatibility you have to override isEqual method as described on page 16 of this document: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/BuildingCocoaApps.pdf