Translating ObjC-Blocks to Swift Closures - ios

I am trying to translate some objective-C Code into Swift. I added the Cocoapod "Masonry" for Autolayout to my project and added a Bridging-Header in order to able to use Objective-C Methods in Swift.
This ObjC Method:
[_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
should be something like the following Closure:
tableView.mas_makeConstraints({ (make : MASConstraintMaker!) -> Void? in
make.edges.equalTo(self.view)
})
But I am getting an "Could not find member 'mas_makeConstraints'" which is not the error, as the method is indexed and autocomletion gives me the following:
tableView.mas_makeConstraints(block: ((MASConstraintMaker!) -> Void)?)
?!
Am I doing something wrong here?

Just my 2 cents if anyone encounter this case:
This objc
[productView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view.mas_left).with.offset(15);
make.right.equalTo(self.view.mas_right).with.offset(15);
make.top.equalTo(self.view.mas_top).with.offset(15);
make.height.equalTo(productView.mas_width);
}];
will turn into
productView.mas_makeConstraints{ make in
make.left.equalTo()(self.view.mas_left).with().offset()(15)
make.right.equalTo()(self.view.mas_right).with().offset()(-15)
make.top.equalTo()(self.view.mas_top).with().offset()(15)
make.height.equalTo()(productView.mas_width)
return ()
}

This piece here in the method signature:
(block: ((MASConstraintMaker!) -> Void)?)
...is telling you that the block argument is Optional, not the return value, which should be Void (not Void?, where you write (make: MASConstraintMaker!) -> Void?)
also: because Swift Type Inference you don't need to put the types in the block
also also: because Swift Trailing Closures you don't need to put a closure that is the final argument to a method inside of the argument list in parens (and since here it's the only argument, you can leave off the parens entirely)
so really your entire method call with block argument could be re-written as:
tableView.mas_makeConstraints { make in
make.edges.equalTo(self.view)
}
finally: it looks like the instance method you are calling on make.edges returns a block, and thanks to the Swift convenience feature of 'implicit return of single expression blocks' it's may be the case that your block is implicitly returning the value of that expression when it is expecting Void - so in the end, if the above doesn't work you may still need to explicitly return Void by writing your method call as:
tableView.mas_makeConstraints { make in
make.edges.equalTo(self.view)
return ()
}

Related

Calling block from Objective C collection in Swift

I have a Swift object that takes a dictionary of blocks (keyed by Strings), stores it and runs block under given key later at some point depending on external circumstances (think different behaviours depending on the backend response):
#objc func register(behaviors: [String: #convention(block) () -> Void] {
// ...
}
It's used in a mixed-language project, so it needs to be accessible from both Swift and Objective-C. That's why there's #convention(block), otherwise compiler would complain about not being able to represent this function in Objective-C.
It works fine in Swift. But when I try to invoke it from Objective-C like that:
[behaviorManager register:#{
#"default": ^{
// ...
}
}];
The code crashes and I get following error:
Could not cast value of type '__NSGlobalBlock__' (0x...) to '#convention(block) () -> ()' (0x...).
Why is that, what's going on? I thought #convention(block) is to specifically tell the compiler that Objective C blocks are going to be passed, and that's exactly what gets passed to the function in the call.
That's why there's #convention(block), otherwise compiler would
complain about not being able to represent this function in
Objective-C
For the sake of consistency: commonly you use #convention attribute the other way around - when there is an interface which takes a C-pointer (and implemented in C) or an Objective-C block (and implemented in Objective-C), and you pass a Swift closure with a corresponding #convention as an argument instead (so the compiler actually can generate appropriate memory layout out of the Swift closure for the C/Objective-C implementation). So it should work perfectly fine if it's Objective-C side where the Swift-created closures are called like blocks:
#interface TDWObject : NSObject
- (void)passArguments:(NSDictionary<NSString *, void(^)()> *)params;
#end
If the class is exposed to Swift the compiler then generates corresponding signature that takes a dictionary of #convention(block) values:
func passArguments(_ params: [String : #convention(block) () -> Void])
This, however, doesn't cancel the fact that closures with #convention attribute should still work in Swift, but the things get complicated when it comes to collections, and I assume it has something with value-type vs reference-type optimisation of Swift collections. To get it round, I'd propose to make it apparent that this collection holds a reference type, by promoting it to the [String: AnyObject] and casting later on to a corresponding block type:
#objc func takeClosures(_ closures: [String: AnyObject]) {
guard let block = closures["One"] else {
return // the block is missing
}
let closure = unsafeBitCast(block, to: ObjCBlock.self)
closure()
}
Alternatively, you may want to wrap your blocks inside of an Objective-C object, so Swift is well aware of that it's a reference type:
typedef void(^Block)();
#interface TDWBlockWrapper: NSObject
#property(nonatomic, readonly) Block block;
#end
#interface TDWBlockWrapper ()
- (instancetype)initWithBlock:(Block)block;
#end
#implementation TDWBlockWrapper
- (instancetype)initWithBlock:(Block)block {
if (self = [super init]) {
_block = block;
}
return self;
}
#end
Then for Swift it will work as simple as that:
#objc func takeBlockWrappers(_ wrappers: [String: TDWBlockWrapper]) {
guard let wrapper = wrappers["One"] else {
return // the block is missing
}
wrapper.block()
}

This is one thing I do not understand in Swift

Consider these lines:
I create a NSButton based class with this:
typealias onClickHandler = (NSTextfieldSuper)->Void
var onClick: onClickHandler?
When the user clicks on an instance of that button, I do this:
if (self.onClick != nil) {
onClick?(self)
}
I use that button later, from another class, with this:
let button = SuperButton()
button.onClick = { (textField: NSTextfieldSuper)->Void in
}
I am not sure if this is the correct syntax. I would like to process the button sent from the first closure on the parent class, where the button is created.
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
I just want to process that button sent.
By the way, as a bonus, I have to initialize several buttons with this, all running the same function. It would be nice to do something like
func doSomething () {
}
and then
let button = SuperButton()
button.onClick = doSomething
any ideas?
This was the only form I was able to type this without Xcode complaining. If this is correct, what is the purpose of this ->Void there? What could this possibly returning?
It is the same as in your typealias, in Swift a function type has the form:
(parameter definitions) -> return type
and functions which return nothing have a return type of Void (similar to C). The full form off a closure expression is:
{ (parameter definitions) ->return typeinbody}
Without any inference this expression provides the full type of the closure, and the -> Void Return type in your example specifies that your closure returns nothing. In your assignment this full type will be checked at compile time to conform to the type of onClick.
Now Swift will infer lots of stuff and there are various shorthands available for closure expressions, you will find that Swift accepts:
button.onClick = { textField in }
as well here with both the argument and return types of the closure being inferred.
By the way, as a bonus, [...] any ideas?
Just make the types match:
func doSomething(textField : NSTextfieldSuper) { }
button.onClick = doSomething
Unlike in (Objective-)C functions and closures (blocks in C) are interchangeable (as they are in plenty of other languages, C is the oddfellow here)
HTH

Getter for object property needs to return UnsafeMutablePointer<T>?

I'm working in Swift and one of the protocols I'm using needs to return an UnsafeMutablePointer<T> of a particular object.
I have something like this:
#objc var myProperty:UnsafeMutablePointer<someObject>
{
get
{
// I call a class function here to get a 'someObject'
// return object which I need to pass back a pointer to it.
return UnsafeMutablePointer<someObject>
}
}
The problem is that Xcode doesn't like this. It complains that '>' is not a unary operator.
I've also tried removing the UnsafeMutablePointer<> and use an & in front of someObject but it complains that the & is to be used immediately in a list of arguments for a function.
I suppose I just can't find the right syntax for this? Any help would be appreciated.
If someObject has the type SomeClass, then you need to update your declaration like this:
#objc var myProperty:UnsafeMutablePointer<SomeClass>
{
get
{
return UnsafeMutablePointer<SomeClass>(unsafeAddressOf(someObject))
}
}
The generic argument needs to be the type of the returned data, and you also need to intialize a specialized UnsafeMutablePointer with the memory address of the desired object.

Why will func closures sometimes delete its paramter name in Swift?

I have a function of two closures
testNetworkAvailability(reachableBlock:, unreachableBlock:)
But when I hit enter for the autocompletion of closure placeholder, the second one unreachableBlock will delete the variable name along with it and causes an error.
For example, if I open up this closure placeholder by hitting enter, it will look like:
testNetworkAvailability(reachableBlock: { () -> Void in
<#code#>
}) { () -> Void in
<#code#>
}
As a matter of fact, as I copy this function to stackoverflow, the placeholder for these blocks reads as <#(() -> Void)?##() -> Void#>. It is so strange as it should be #() -> Void# only, shouldn't it?
Why is this and how to fix it?
As long as the last argument is a closure, Swift allows you to omit the parameter name and treat it as an inline block.
autoreleasepool {
// ...
}
See the documentation on trailing closures.
Should XCode's autocomplete prefer trailing closures than not is a topic for debate however.

Swift closure syntax using Shark Food Mute Switch?

I am having trouble with swift closure syntax. I am trying to check the mute switch using Sharkfood which you can see here: http://sharkfood.com/content/Developers/content/Sound%20Switch/
The Block I'm trying to call is shown below.
typedef void(^SharkfoodMuteSwitchDetectorBlock)(BOOL silent);
This is how I'm trying to call it but it isn't working. I've tried a ton of different ways and I know I'm missing something little since I'm new to swift. The error I'm getting is:
'(Bool) -> Void' is not convertible to 'Bool'
On the first line of this code:
muteDetector.silentNotify({
(silent: Bool) -> Void in
println("this")
println("worked")
})
Any help would be greatly appreciated.
Never used that library, but looking at the documentation linked in your question I notice that silentNotify is a property:
#property (nonatomic,copy) SharkfoodMuteSwitchDetectorBlock silentNotify;
containing the block, so the error stating that a BOOL is expected makes sense.
Instead with your code you are probably trying to call this method:
-(void)setSilentNotify:(SharkfoodMuteSwitchDetectorBlock)silentNotify{
_silentNotify = [silentNotify copy];
self.forceEmit = YES;
}
I don't know which of the 2 you are attempting to do - if you want to call the block, then you have to just provide a bool:
muteDetector.silentNotify(true)
if instead you want to register a new block (closure), then you have to use:
muteDetector.setSilentNotify({
(silent: Bool) -> Void in
println("this")
println("worked")
})

Resources