How does this #define work in Swift? - objective-c

I am new to iOS and am trying to tackle learning Swift (and a bit of Objective C) by converting a library I found to the former. So far I'm doing alright, but am having trouble understanding one part; how this #define is supposed to work:
#define RunSafeBlock(block, ...) block ? block(__VA_ARGS__) : nil
Here it is with more context:
#import <AFNetworking.h>
// How would I write this in Swift?
#define RunSafeBlock(block, ...) block ? block(__VA_ARGS__) : nil
#interface Client ()
#end
#implementation Client
- (void)requestWithBodyBlock:(void (^)(id<AFMultipartFormData> formData))bodyBlock completion:(ClientRequestCompletion)completion {
[self validateAccessToken:^(NSError *error) {
if (error) {
// What does RunSafeBlock do?
RunSafeBlock(completion, nil, error);
return;
}
// ...
}];
}
#end
The AFNetworking parts are not really relevant, but in this example we're performing a request and validating an access token. If validateAccessToken returns an error, we pass everything to RunSafeBlock and exit out. Here we are in Swift:
import AFNetworking
// #define RunSafeBlock(block, ...) block ? block(__VA_ARGS__) : nil
class Client {
func requestWithBodyBlock(bodyBlock: (formData: AFMultipartFormData) -> Void, completion: ClientRequestCompletion) {
self.validateAccessToken({(error: NSError) -> Void in
if error != nil {
// RunSafeBlock(completion, nil, error)
return
}
// ...
})
}
}
I would really appreciate some guidance. Thanks in advance!

This macro is doing a null-check.
In Swift, unless you declare a variable as Optional, it cannot be null, so you do not have to do this check in Swift (and if it was Optional, you'd use optional unwrapping).
So just go ahead and call the completion.
(It seems the same reasoning would also apply to error. Don't you get a compile error there?)
(Also consider using the Swift version of AlamoFire).

Related

Is it possible that Bridging-header turns (void (^)(NSError *))block (ObjC) into block: () throws -> () (Swift)?

I have
OBJC:
- (void)doSomething:(void (^)(NSError *))block;
SWIFT:
let test = Test()
test.doSomething(<#T##block: ((Error?) -> Void)!##((Error?) -> Void)!##(Error?) -> Void#>)
I would rather
try? test.doSomething { }
I would like bridging-header to translate the function into
func doSomething(block: () throws -> ()) throws {
try block()
}
Is it possible? Thanks to all!
Your Objective-C method is declaring a parameter which is a block that receives an NSError object. It's basically declaring a callback.
If your method is not asynchronous you should declare it like this:
- (BOOL)doSomething:(NSError **)error;
Now the parameter is a pointer to an NSError* object. If the method fails for some reason, it should set an appropriate error for that parameter like so:
if (error != NULL) {
*error = <an appropriate error>
}
Also note the BOOL return type. According to Cocoa conventions the caller should refer to the return type to determine if the method failed or not, instead of testing for the existence of an NSError* object.
Declaring a method like this will expose it in Swift using the throws mechanics.
Update:
I don't think you can declare a Swift throwing block in Objective-C. If you go the other way around, and declare your desired method signature in Swift you'll see the compiler complains it can't be represented in Objective-C.
Most likely the (NSError **) to throwable convention never got implemented for blocks.

Using Obj-C completion block in Swift

In Objective-C, I have a completion block class defined as:
File.h
typedef void (^MYCompletionBlock)(BOOL success, NSDictionary *result, NSError *error);
Then, in a Swift file, I try to use the completion block as follows:
Swift.swift
class MyClass: NSObject{
...
func MyFunction() -> Void {
...
objcMethod(param1, withCompletion: {(MYCompletionBlock) -> Void in
if (success){ // Error:"Use of unresolved identifier 'success'"
}
}
...
}
...
}
But, I keep getting an error: "Use of unresolved identifier 'success'".
I've tried the following as well:
objcMethod(param1, withCompletion: {(success:Bool, result: NSDictionary, error:NSError) -> Void in
if (success){ // Error:"Cannot convert value of type '(Bool, NSDictionary, NSError) -> Void' to expected argument type "MYCompletionBlock!"
}
}
Can somebody help me understand how to correctly specify a Obj-C completion block in Swift?
Given that your closure doesn't specify nullability qualifiers (where they almost certainly are optional), one can safely assume that your Objective-C API has not been audited for nullability. Thus, Swift will treat pointers as implicitly unwrapped optionals. Furthermore, nowadays the NSDictionary is mapped to a [NSObject : AnyObject] Swift dictionary.
Thus, it would be:
obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]!, error: NSError!) in
if success {
// do something
}
}
Or, as Kobi points out, you can let the compiler infer the types:
obj.objcMethod(param) { success, result, error in
if success {
// do something
}
}
Note, you don't have to remember this yourself. You can leverage Xcode's code completion as you enter the code. So, type enough to match the method name and when it matches objcMethod, then hit enter:
When you get to MYCompletionBlock, hit enter again, and it will show you the correct signature:
If this Objective-C method was my own class, I would audit it for nullability. So, for example, let's assume the param is optional, the closure is required, and the result and error were optional, you might define it like so:
NS_ASSUME_NONNULL_BEGIN
typedef void (^MYCompletionBlock)(BOOL success, NSDictionary * _Nullable result, NSError * _Nullable error);
#interface MyObject : NSObject
- (void)objcMethod:(NSDictionary * _Nullable)param1 withCompletionHandler:(MYCompletionBlock)completionHandler;
#end
NS_ASSUME_NONNULL_END
And, if that was the case, your Swift code would call it like so:
obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]?, error: NSError?) in
if success {
// do something
}
}
Or, again, just let the compiler infer the types for you (but this time they'd be inferred as optionals that are not implicitly unwrapped):
obj.objcMethod(param) { success, result, error in
if success {
// do something
}
}
You shouldn't specify types for the completion block parameters, as some types defer between Swift and Objective C (e.g. BOOL is actually ObjCBool in Swift).
This should work:
objcMethod(param1) { (success, result, error) in
if (success){
// Do something
}
}

Checking if [NSObject : AnyObject]! is NSNull in Swift

My project uses both Swift and Objective C. I have a singleton written in Objective C which as a property of the type NSDictionary.
#property(nonatomic, strong) NSDictionary *currentDictionary;
This is an old class that's being used throughout my project. I am trying to use this class in a Swift class like this
if let dict = DataManager.sharedManager().currentDictionary
{
//
}
The problem i am facing is that currentDictionary is being set using data from the server. At times this might be
In Objective C classes, i can handle the situation with the following check
if ([currentDictionary isKindOfClass: [NSNull class]])
{
// Do nothing
}
But i am not sure how to implement a similar check in Swift. I tried the following
if let data = DataManager.sharedManager().currentDictionary as? NSNull
{
return
}
But it doesn't work and also i get a compiler warning :
"Cast from "[NSObject : AnyObject]!" to unrelated type "NSNull" always fails"
This is different from checking for Null values within the dicitonary as they will be 'AnyObject's and i can try casting them into the type i want to check.
Can someone please provide any pointers on how to handle this situation properly
First of all, if the variable can contain something else that NSDictionary, don't set its type to NSDictionary. Swift is type safe and it will trust the declared type.
The easiest workaround would be to make it id in Objective-C.
Then in Swift you can simply:
guard let data = DataManager.sharedManager().currentDictionary as? NSDictionary else {
return
}
If you can't change the original code, just create a Swift accessor with correct type using a category, e.g.
#interface DataManager (Swift)
// solution 1, will be converted to AnyObject in Swift
- (id)currentDictionaryForSwift1;
// solution 2, let's handle NSNull internally, don't propagate it to Swift
- (NSDictionary *)currentDictionaryForSwift2;
#end
#implementation DataManager
- (id)currentDictionaryForSwift1 {
return self.currentDictionary;
}
- (NSDictionary *)currentDictionaryForSwift2 {
if (self.currentDictionary == [NSNull null]) {
return nil;
}
return self.currentDictionary;
}
#end
I would recommend you to handle NSNull internally. There should be no need to for other code to handle nil and NSNull separately.
You could actually solve it already in the getter:
- (NSDictionary *)currentDictionary {
if (_currentDictionary == [NSNull null]) {
return nil;
}
return _currentDictionary;
}
or in the setter
- (void)setCurrentDictionary:(NSDictionary *)currentDictionary {
if (currentDictionary == [NSNull null]) {
_currentDictionary = nil;
} else {
_currentDictionary = currentDictionary;
}
}
As you can see, there are multiple solutions but the best solution should improve even your Obj-C code. The difference between NSNull and nil should be handled locally and not propagated.
If you want to validate wether currentDictionary is nil or not, you can use:
guard let currentDictionary = DataManager.sharedManager().currentDictionary else {
return
}
Replace guard-else statement with if-else if you don't want to return early.
If you want to validate contents of currentDictionary is NSNull or not:
if let value = DataManager.sharedManager().currentDictionary["key"] {
// do something with value
}

Calling Objective-C method from C code

I'm trying to call a method implemented in Obj-C from C code as follows:
// MainViewController.m
- (void)Test
{
[self outputLine:#"Called from MyCode.c"];
}
.
.
.
cFunc(id param);
.
.
.
// MyCode.c
void cFunc(id param)
{
[param Test]; // compilation error: Parse issue: Expected expression
}
I guess it happens since the MainViewController is not declared in MyCode.c, but when I #include the MainViewController.h I get tons of other errors that suggests I'm totally wrong...
How should I handle it correctly?
TIA
You should compile the MyCode.c file as Objective-C. Objective-C is a superset of C, but it's not true the other way around. You can't have Objective-C code interspersed with your "pure C" code if you are intending to compile it as C.
Check: using objc_msgSend to call a Objective C function with named arguments
void cFunc(id param) {
objc_msgSend(param, sel_getUid("Test"));
}
But, as per the link above, this is dangerous for a few reasons, if your arguments don't fit in registers (i.e. floats, structs, blah blah).
The accepted way of doing this is to cast objc_msgSend:
void cFunc(id param) {
// Now let's pretend that you want to send someFloat to your method
void (*objc_msgSendTyped)(id self, SEL _cmd, float bar) = (void*)objc_msgSend;
float someFloat = 42.f;
objc_msgSendTyped(param, sel_getUid("Test"), someFloat);
}
Just change your myCode.c to myCode.m :P
Don't be afraid to put C code in an Objective-C file.

Can't use dispatch_sync in c++ class with Objective-C++ code

I need to use Objective-C++ code in inherited C++ class which works with video recording from iPhone camera (getting CMSampleBufferRef through an other native-objective-c class Wrapper with CMSampleBufferDelegate).
The AVCaptureVideoOutput that i have works in its own dispatch_queue_t callbackQueue, so, when i want to get the last frame from my Wrapper class, I need to lock the callbackQueue to make it wait till the copying will be done.
As i know, it's done with dispatch_sync, syncing the captureOutput.callbackQueue. But i can't get this code working:
// .mm
frame_t MyCppClass::getLastFrame()
{
dispatch_sync(pCaptureVideoDataOutput.sampleBufferCallbackQueue, ^{ // error: no matching function for call to 'dispatch_sync'
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(wrapperInstance->currentBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0);
// doing copying frame data from buffer...
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
}); // error: control may reach end of non-void block
return frame;
}
// .h
#interface Wrapper : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> {
CMSampleBufferRef currentBuffer;
}
#end
// .mm
#implementation Wrapper
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// doing copying to currentBuffer
}
#end
EDIT:
When i changed to
dispatch_sync(pCaptureVideoDataOutput.sampleBufferCallbackQueue, (dispatch_block_t)^{
it fixed the first error, but the second is still here..
Got stuck with this.. any help is appreciated!
I figured it out!
I've got some emergency return statements inside the block. I thought that it will return the function, but it returns the block... so compiler was right.
The error:
error: no matching function for call to 'dispatch_sync'
indicates that the function prototype may not be visible. Make sure you are including the header for libdispatch in your .mm file...
#include <dispatch/dispatch.h>
The second error:
error: control may reach end of non-void block
is because your function is declared to return a frame_t but it has no return statement.