Casting Objective C blocks in Swift - objective-c

There is a particular case where I can't find the correct solution on how to cast a block defined in Objective C to a closure in Swift, so that I can execute it in Swift. For other cases using #escaping helped when having another block/closure being passed to a closure. But unfortunately not in this case.
Block type definitions in Objective C:
typedef void (^ViewModelUpdatedBlock)(GetValueForKeyPathBlock);
typedef id (^GetValueForKeyPathBlock)(NSString * keyPath);
Declaration of a block in Objective C:
someNSMutableDictionary[kViewModelUpdatedBlockKey] = ^(GetValueForKeyPathBlock getValueBlock) { ... }
When I try to use one of these blocks in Swift I can't find the correct way to cast it. I tried:
let viewModelUpdatedBlock = someNSMutableDictionary[kViewModelUpdatedBlockKey] as? ViewModelUpdatedBlock // = nil
let viewModelUpdatedBlock = someNSMutableDictionary[kViewModelUpdatedBlockKey] as? (GetValueForKeyPathBlock?)->() // = nil
let viewModelUpdatedBlock = someNSMutableDictionary[kViewModelUpdatedBlockKey] as? (GetValueForKeyPathBlock)->() // = nil
let viewModelUpdatedBlock = someNSMutableDictionary[kViewModelUpdatedBlockKey] as? (#escaping GetValueForKeyPathBlock)->() // = nil
Please help!

Related

Create Swift optionals from cast NSDictionary object

I'm trying to cast an Objective-C NSDictionary's values to 2 Swift optionals.
These values may be missing from the original NSDictionary, in which case the function can safely be called with nil arguments.
Is there a better way of doing this?
#objc
track(_ data: NSDictionary) {
// Cast NSDictionary to Swift dictionary
guard let dataDictionary = data as? [String: Any] else {
return
}
// Extract (optional) values
let value = dataDictionary["value"]
let name = dataDictionary["name"]
// Cast Objective-C optionals if possible, otherwise assign nil
let floatValue: Float? = (value as? CGFloat != nil) ? Float(value as! CGFloat) : nil
let stringName: String? = (name as? NSString != nil) ? name as! String : nil
// Function arguments:
// name: String? = nil
// value: Float? = nil
Tracker.track(name: stringName, value: floatValue)
}
Firstly
let stringName: String? = (name as? NSString != nil) ? name as! String : nil
Can be simplified as
let stringName: String? = name as? String
But if we want to make the modification earlier, we can avoid the [String: Any] from the start:
let floatValue = (data["value"] as? NSNumber)?.floatValue
let nameValue = data["name"] as? String
If data["value"] doesn't exist, it will be nil. If it's not a NSNumber (since it's the Objective-C basic way to encapsulate Float into a Object) it will be nil. In the end, the floatValue will be nil or have the real value.
Same logic for the nameValue.

convert (void *) to (SInt16 *) to Swift

I'm trying to convert the following from Objective-C to Swift:
-(int)fillBuffer:(void *)buffer {
SInt16* p = (SInt16 *)buffer;
// ...
p[33] = 0
}
I've come to understand that (void *) maps to the UnsafeMutableRawPointer? type in Swift.
However, I'm missing a step in converting it into something that can take the subscript operation.
So far, I've got this:
func fill(buffer: UnsafeMutableRawPointer!) -> Int {
buffer[33] = 0
}
Looking for feedback and advice. Thanks in advance!
Casting a void pointer to a typed pointer
SInt16* p = (SInt16 *)buffer;
is done in Swift with assumingMemoryBound():
func fillBuffer(_ buffer: UnsafeMutableRawPointer) -> Int {
let p = buffer.assumingMemoryBound(to: Int16.self)
// ...
p[33] = 0
return 0
}
Test code:
var i16Array = Array(repeating: Int16(99), count: 40)
print(i16Array[33]) // 99
_ = fillBuffer(&i16Array) // passes a pointer to the element storage to the function
print(i16Array[33]) // 0

Objective-C to Swift translation issue

I'm trying to translate this simple line of code to Swift, but can't figure out how to write it:
AVCaptureConnection *videoConnection = nil;
I've tried:
let videoConnection: AVCaptureConnection = nil
let videoConnection: AVCaptureConnection = false
var videoConnection:AVCaptureConnection = AVCaptureConnection()
videoConnection = nil
var videoConnection:AVCaptureConnection = AVCaptureConnection()
videoConnection = false
var videoConnection:AVCaptureConnection = AVCaptureConnection()
videoConnection.active = false
var videoConnection:AVCaptureConnection = AVCaptureConnection()
videoConnection.active = nil
Any suggestions on how to write this is Swift would be appreciated.
If you want to "initialize" something with nil it has to be an Optional.
So
var videoConnection: AVCaptureConnection? = nil
or
var videoConnection: AVCaptureConnection?
would be right.
I believe it would be:
let videoConnection: AVCaptureConnection = UnsafePointer<Int8>.null()
Since Swift sort of conceals pointers from you (except that objects are really pointer references to the objects) and direct pointer manipulation, you sometimes have to contort yourself a bit. :)

Block from Objective-C to Swift

I used a framework Objective-C in my Project (Swift). But in the code have a Block, i cannot convert to swift (i'm newbie in swift)
So the code is
[self.datePicker setDateHasItemsCallback:^BOOL(NSDate *date) {
int tmp = (arc4random() % 30)+1;
return (tmp % 5 == 0);
}];
Please help me.
Thank you ,
Where you would use a block in Objective-C, you use a function in Swift. In Objective-C, the argument is a block that takes an NSDate and returns a BOOL:
[self.datePicker setDateHasItemsCallback:^BOOL(NSDate *date) {
So, in Swift the argument is a function that takes an NSDate and returns a Bool:
self.datePicker.setDateHasItemsCallback {
(date:NSDate) -> Bool in
return true // fix this up as desired
}

How can method in Swift with inout parameter be used in Objective-C?

I want
func foo(inout stop: Bool) -> Void {
// ...
}
use in my Objective-C part. But it is never generated in Module-Swift.h header. If I mark it with #objc, the
Method cannot be marked #objc because the type of the parameter cannot
be represented in Objective-C
error occurs.
You can't use an inout parameter when bridging with Objective-C, but you can do something similar if you use an UnsafeMutablePointer<T> (T would be Bool in your case). It would look something like this:
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
if stop != nil {
// Use the .pointee property to get or set the actual value stop points to
stop.pointee = true
}
}
Example
TestClass.swift:
public class TestClass: NSObject {
#objc func foo(stop: UnsafeMutablePointer<Bool>) -> Void {
stop.pointee = true
}
}
Objective-C:
TestClass *test = [[TestClass alloc] init];
BOOL stop = false;
[test foo:&stop];
// stop is YES here
Similarly to what happening with generics, inout is not objc-compatible.
One possible workaround is to embed your parameter(s) in a class (which is a reference type, hence passed by pointer and not by value):
#objc class MyFuncParams {
var stop: Bool
init(stop: Bool) {
self.stop = stop
}
}
and define the function to accept an instance of that class:
func changeParam(params: MyFuncParams) {
params.stop = true
}
Not an elegant way to solve the problem, but what's important is that it should work (never tried myself though).