Swift 3.0 XCTest.performTest(_:) #selector - selector

In Swift 2.3 [XCTest performTest:] was translated to Swift as public func performTest(run: XCTestRun)
In Swift 3.0 it became open func perform(_ run: XCTestRun)
With this change I'm stuck trying to get the method selector (even following suggestions https://bugs.swift.org/browse/SR-1016 and https://bugs.swift.org/browse/SR-1033).
#selector(XCTest.perform(_:)) -> matches NSObject's performSelector
#selector(XCTest.perform as (XCTestRun) -> Void) -> ambiguous reference to member 'perform'
#selector(XCTest.perform(_:) as (XCTestRun) -> Void) -> cannot convert value of type '(Selector!) -> Unmanaged<AnyObject>!' to type '(XCTestRun) -> Void' in coercion
How do I get the performTest selector properly?

Alas, the type of XCTest.perform(_:) is curried, so one will need the ugly:
#selector(XCTest.perform(_:) as (XCTest) -> (XCTestRun) -> Void)

Related

Xcode 10.2 Swift Error: Function types cannot be represented in Objective-C unless their parameters and returns can be

I updated Xcode to 10.2 today and I got the following errors:
Method cannot be marked #objc because the type of the parameter 2
cannot be represented in Objective-C
Function types cannot be represented in Objective-C unless their parameters and returns can be
I don't understand why
It was perfectly fine in 10.1.
This is an example that I have been using for years without any issues.
How can I make this code to compile without errors?
#objc public func myFunction(inputString: String, handler:#escaping ((success: Bool, outPut: NSArray)) -> Void) {
// do stuff
}
Delete the phrase outPut:. It was always illegal; Swift 5 just tightens up at last.
So:
#objc public func myFunction(inputString: String, handler:#escaping (NSArray) -> Void) {

How do I call an Swift method with a closure from Objective-c?

I have the following Swift method:
public func login(userLogin: String, userPassword: String, completion:#escaping (_ result: Result<Data, PZError>) -> ())
And I would like to call it from Objective-C but can't figure out the closure portion:
[apiController loginWithUserLogin:userLogin userPassword:password completion:???];
What would my completion code look like in Objective-C?
It is not possible to call such a method directly from Objective-C.
If you Result type is an enum with non-trivial "case" objects or a struct, it won't work, because Objective-C doesn't support value types.
Also Objective-C doesn't support generic types like Result<T>.
One way to fix would be to make a wrapper, i.e. write a second method in Swift that adapts the types:
func login(userLogin: String, userPassword: String,
completion: #escaping (_ resultData: Data?, _ resultError: NSError?) -> Void)
In this method you would call your original login, and in the internal completion callback unwrap your Result into Data and NSError (converting from PZError), and pass 'em the caller's completion block.

Rust trait object's &self cannot be used in a trait default function

Trying to override a trait cast problem, described here. Stuck at implementing the trait function which returns the enum instance with own implementation inside:
//the "trait matcher" enum
enum Side<'a> {
Good(&'a GoodDude),
Bad(&'a BadDude),
}
//very general trait
trait Dude {
fn who_am_i(&self) -> Side;
fn do_useful_stuff(&self);
}
//specific trait #1
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self)
}
fn save_the_world(&self);
}
//specific trait #2
trait BadDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Bad(&self)
}
fn do_evil(&self);
}
But for some reason the compilation of this part fails with E0277:
trait GoodDude: Dude {
fn who_am_i_inner(&self) -> Side {
Side::Good(&self) //&self should be &GoodDude, but compiler says it is not...
}
fn save_the_world(&self);
}
And results in:
<anon>:16:20: 16:25 error: the trait `GoodDude` is not implemented for the type `&Self` [E0277]
<anon>:16 Side::Good(&self)
^~~~~
<anon>:16:20: 16:25 help: see the detailed explanation for E0277
<anon>:16:20: 16:25 note: required for the cast to the object type `GoodDude`
Can this be worked out?
Full sample: https://play.rust-lang.org/?gist=8ae2384e401da76c16214c4a642ce8b4&version=stable&backtrace=0
Firstly, the type of self in fn who_am_i_inner is already a reference, so you don't need to &.
fn who_am_i_inner(&self) -> Side {
Side::Good(self)
}
But then rustc complains...
<anon>:13:20: 13:24 error: the trait `core::marker::Sized` is not implemented for the type `Self` [E0277]
<anon>:13 Side::Good(self)
^~~~
<anon>:13:20: 13:24 help: see the detailed explanation for E0277
<anon>:13:20: 13:24 note: `Self` does not have a constant size known at compile-time
<anon>:13:20: 13:24 note: required for the cast to the object type `GoodDude`
Admittedly the error message is very unclear and E0277 is about something totally different. Let's try the nightly compiler instead, which gives better error messages:
error: the trait bound `Self: std::marker::Sized` is not satisfied [--explain E0277]
--> <anon>:13:20
13 |> Side::Good(self)
|> ^^^^
help: consider adding a `where Self: std::marker::Sized` bound
note: required for the cast to the object type `GoodDude`
OK let's try to add the where Self: Sized:
fn who_am_i_inner(&self) -> Side where Self: Sized {
Side::Good(self)
}
and now it works.
World saved. Press any key to continue
May the 4th be with you
Pew Pew Pew
Luke I am yr father
The where Self: Sized is Rust's way to signify that the method cannot be used from trait objects. We say the method ignored from "object-safety", or "cannot be virtual" if you like C++.
The effect is that if all you have got is luke: &GoodDude, then you cannot call luke.who_am_i_inner() since *luke has an unknown size.
The reason we need to make the method not object-safe is due to the cast &Self → &GoodDude. In Rust a trait object reference like &GoodDude is a fat pointer, internally it is represented as a 2-tuple (pointer, method_table). However, in a trait the self is a thin-pointer.
We cannot convert a thin-pointer to a fat-pointer, since there is a missing information, the method_table. This can be filled in if we knew the concrete type. That's why we add the where Self: Sized.
If you want to make who_am_i_inner object-safe, then you cannot provide a default implementation.

Bridging from Objective C to Swift with PromiseKit

Using PromiseKit 2.0 with Swift 1.2, I'm trying to use a PMKPromise that was created in Objective C from Swift.
Objective C code:
#interface FooTest : NSObject
+ (PMKPromise *)promise;
#end
Swift code (I've tried a number of variations, none of which work. This one is closest to the example given at http://promisekit.org/PromiseKit-2.0-Released/):
FooTest.promise().then { (obj: AnyObject?) in
self.obj = obj
}
Compiler error: Cannot invoke 'then' with an argument list of type '((AnyObject?) -> _)'
This doesn't work either:
FooTest.promise().then { (obj: AnyObject?) -> AnyPromise in
return AnyPromise()
}
Similar error: "Cannot invoke 'then' with an argument list of type '((AnyObject?) -> AnyPromise)'"
There are two different promise classes in PromiseKit, one for Swift (Promise<T>) and one for ObjC (AnyPromise). The Swift ones are generic and Objective-C cannot see generic classes, so this is why there are two.
If Foo.promise() is meant to be used in both ObjC and Swift then you are doing the right thing. If however you only intend to use this promise in Swift then I suggest rewriting it as a Promise<T>.
To use an Objective-C AnyPromise (PMKPromise is a deprecated alias for AnyPromise: prefer AnyPromise) in Swift code you must splice it into a an existing chain.
someSwiftPromise().then { _ -> AnyPromise in
return someAnyPromise()
}.then { (obj: AnyObject?) -> Void in
//…
}
There should be a way to start from an AnyPromise, probably I will add this later today:
someAnyPromise().then { (obj: AnyObject?) -> Void in
//…
}
Expect a 2.1 update. [edit: 2.1 pushed with the above then added]

Problems trying to port an Objective C completion block into Swift

I'm using a third party library written in objective C with the following method:
- (void)manageServerResponse:(NSURLResponse*)response NSData:(NSData*)data andNSError:(NSError*)error onComplete:(void (^)(NSInteger kindOfError, NSDictionary*jsonResponse))onComplete;
when I port it to swift I do the following:
typealias ResponseCompletedBlock = (NSInteger, NSDictionary?) -> Void
...
let completedResponseMethod : ResponseCompletedBlock = {(kindOfError: NSInteger, jsonResponse: NSDictionary?) -> Void in
self.onComplete(kindOfError, jsonResponse: jsonResponse)}
let responseManager: ResponseManager = ResponseManager.sharedResponseManager() as! ResponseManager
responseManager.manageServerResponse(response,
NSData: data,
andNSError: error,
onComplete: completedResponseMethod)
I'm getting this error:
Cannot invoke 'manageServerResponse' with an argument list of type
'(NSURLResponse?, NSData: NSData?, andNSError: NSError?, onComplete:
ResponseCompletedBlock)'
and if I replace the last sentence for
responseManager.manageServerResponse(response,
NSData: data,
andNSError: error,
onComplete: nil)
everything works, so I assume that the problem is with the block structure, but I've tried to change everything and the error remains.
Can you help?
NSDictionary * is mapped to Swift as [NSObject : AnyObject]!,
therefore the type of the response block should be
typealias ResponseCompletedBlock = (Int, [NSObject : AnyObject]!) -> Void
and consequently
let completedResponseMethod = {(kindOfError: Int, jsonResponse: [NSObject : AnyObject]!) -> Void in
// ...
}
One method to figure out the correct Swift signature of functions is to use the autocompletion in Xcode:
You start typing
responseManager.manageServerResponse(
and Xcode suggests
Per Apple docs:
When you bridge from an NSDictionary object to a Swift dictionary, the resulting dictionary is of type [NSObject: AnyObject].
Also, unless the Objective-C code is annotated for nullability, the Swift dictionary is an implicitly-unwrapped optional. Therefore your typealias should look like this:
typealias ResponseCompletedBlock = (NSInteger, [NSObject : AnyObject]!) -> Void
I'd also recommend changing the NSInteger to Int.
Is the typealias and completedResponseMethod absolutely necessary for your program to function? If not, this would probably solve your problem:
responseManager.manageServerResponse(response, NSData: data, andNSError: error, onComplete: {
$0, $1 in
self.onComplete($0, jsonResponse: $1)
}
As your error suggests, the error was due to a type mismatch. If you use the default local parameters, you'll be able to work around that particular problem.