Cast Objective-C block NSStackBlock into Swift 3 - objective-c

I'm writing a reactive extension for one of the WKWebView navigation delegates. At the moment I wrote the following code:
public typealias NavigationHttpResponse = (WKWebView, WKNavigationResponse, (WKNavigationResponsePolicy) -> Swift.Void)
public var decidePolicyForNavigationResponse: Observable<NavigationHttpResponse> {
let selector = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:)! as ((WKNavigationDelegate) -> (NavigationHttpResponse) -> ()))
return delegate.methodInvoked(selector)
.map { params in
let webView = params[0] as! WKWebView
let navigationResponse = params[1] as! WKNavigationResponse
let decisionHandler = params[2] as! ((WKNavigationResponsePolicy) -> Swift.Void)
return (webView, navigationResponse, decisionHandler)
}
}
So far so good but when I launch my app add that code is executed, I get a crash when I'm trying to cast the block with the following line:
let decisionHandler = params[2] as! ((WKNavigationResponsePolicy) -> Swift.Void)
In the degub console I get:
Could not cast value of type '__NSStackBlock__' (0x12327d1a8) to '(__C.WKNavigationResponsePolicy) -> ()' (0x12218b8f0).
How can I safely cast from objective-c to Swift?
Or which alternative approach can I take?
Thanks!

Related

Extension can't access generic parameters at runtime

I´m using AWS Cognito, in the doc it says I have to add this function.
But I´m getting this error:
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
extension AWSTask {
public func continueWithExceptionCheckingBlock(completionBlock:#escaping (_ result: Any?, _ error: Error?) -> Void) {
self.continue({(task: AWSTask) -> Any? in
if let exception = task.exception {
print("Fatal exception: \(exception)")
kill(getpid(), SIGKILL);
}
let result: AnyObject? = task.result
let error: NSError? = task.error as NSError?
completionBlock(result, error)
return nil
})
}
}
For those who have this issue in Swift 5, try adding #objc modifier to the method. Please find the example below:
extension NSLayoutAnchor {
#objc func constrainEqual(_ anchor: NSLayoutAnchor<AnchorType>, constant: CGFloat = 0) {
let constraint = self.constraint(equalTo: anchor, constant: constant)
constraint.isActive = true
}
}
Without context I can't say if you are actually doing what the error message prompts you with, but there is an open bug report describing this issue (where no offending code is actually used):
SR-2708: Extending ObjC generics in Swift 3 does not compile
ObjC:
#interface MySet<T : id<NSCopying>> : NSObject
#end
Swift:
class Foo { }
struct Bar { }
extension MySet {
func foo() -> Foo { return Foo() }
func bar() -> Bar { return Bar() }
}
Both of the extension methods result in "Extension of a generic Objective-C class cannot access the class's generic parameters at
runtime". However, neither really does anything like that (at least
not explicitly).
If you read the comments to the bug report, you'll see that a user named 'Vasili Silin' describes having this issue when attempting to extend AWSTask, so you might have to consider alternative approaches until this bug is resolved.
I just got the same error and solve it this way :
extension yourClass where T == yourType {}

Swift 3 Passing a closure via selectors to objc functions

I want to pass a closure through another function via a selector. Here is what I am trying to do ideally:
#objc private func aFunction(_ firstParam: String, onComplete: (String) -> Void) {
//..some internal codes
onComplete("Done.")
}
func functionCaller(_ selectorString: String, paramString: String, onComplete: (String) -> Void) {
let selector : Selector = NSSelectorFromString(selectorString)
self.perform(selector, with: printString, with: onComplete)
}
functionCaller("aFunction:onComplete:", paramString: "anotherParameter", onComplete: { (_ myString String) -> Void in
print(myString)
})
Here the problem is when you try to compile this, Swift gives an error called "Segmentation Fault: 11"
I found the problematic line which is:
self.perform(selector, with: printString, with: onComplete)
when I change last with: onComplete parameter to a String (also changed related functions params) it is working. As far as I understand that the problem is sending closure via self.perform call doesn't work because the first function is an '#objc' marked function (I put this because otherwise perform selector did not work on Swift 3).
So any idea how can I pass a function or closure into a '#objc' marked function via performing selector?
Try to use Any instead of String in your function
func functionCaller(_ selectorString: String, paramString: String, onComplete: (Any) -> Void) {
let selector : Selector = NSSelectorFromString(selectorString)
let complete : Any = onComplete("complete")
self.perform(selector, with: complete)
}
functionCaller("aFunction:onComplete:", paramString: "anotherParameter", onComplete: { (_ myString ) -> Void in
let string = myString as! String
print(string)
})
I tested it and works in Swift 3
let completionHandler: (Any) -> Void = { value in
let js = "try {(callBackKey)('(value)'); delete window.(callBackKey);} catch(e){};"
(jsContext as? TYWKWebView)?.evaluateJavaScript(js, completionHandler: nil)
}
let handlerDict: [String: Any] = [TYJSBridge.COMPLETION_HANDLER_KEY: completionHandler]
let _ = jsInterfaceObject.perform(sel, with: parameters, with: handlerDict)
}

Could not cast value of type XX to XX in swift 3

In Swift 3 contains method is always giving error. In the below code if annotation is MKAnnotation is passed and goest to next line. Then it gives error. I have searched a lot but not able to find the problem. Any solution for this issue?
Class declaration:
open class FBAnnotation : NSObject {
open var coordinate = CLLocationCoordinate2D(latitude: 52.0936440, longitude: 4.3592340)
open var title: String? = ""
open var annotationIndex: Int?
}
extension FBAnnotation : MKAnnotation {
}
Usage:
do {
if annotation is MKAnnotation {
if try node.annotations.contains(where: annotation as! (MKAnnotation) throws -> Bool) {
try node.annotations.remove(at: node.annotations.index(where: annotation as! (MKAnnotation) throws -> Bool)!)
node.count -= 1
return true
}
}
} catch {
return false
}
This works in a playground, no casting required. You cannot pass an annotation as the where parameter. You must pass in a function that declares whether or not the annotation is the one you are looking for. Here I consider them a match if they have the same coordinates, although your match criteria may be different.
var annotations = [MKAnnotation]()
var annotation: Any? = nil
if let annotation = annotation as? MKAnnotation {
if let index = annotations.index(where: {
$0.coordinate.latitude == annotation.coordinate.latitude &&
$0.coordinate.longitude == annotation.coordinate.longitude
}) {
annotations.remove(at: index)
}
}

Objective-C calling parameterized Swift method crashes Swift compiler

I have a simple Swift extension on NSManagedObject, in which I have a parametrized method for finding a single object - the signature looks like:
public class func findFirst<T:NSManagedObject>(inContext context : NSManagedObjectContext? = .None) -> T?
I'm trying to call this from Objective-C, but it seems like it cannot be seen. If I create a non-parameterized version I can see and call it just fine from Objective-C:
public class func findFirstUntypedWithPredicate(predicate:NSPredicate?, inContext context : NSManagedObjectContext? = .None) -> NSManagedObject?
Is there any way for ObjectiveC to be able to reach the parameterized version of the call?
I would use Self like so:
public class func findFirst(inContext context : NSManagedObjectContext? = .None) -> Self?
using the technique found here:
How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
However, that causes the Swift compiler to segfault when compiling the code (Xcode 6.3.1, or Xcode 6.4 beta 2).
Edit: Here's a link with the full source of the framework I'm trying to build, including bonus Swift compiler crashes caused by templated methods:
https://www.dropbox.com/s/fixaj9ygdoi4arp/KiGiCoreData.zip?dl=0
Generic methods are not visible from Objective-C. However you can use
the ideas from How to use generic types to get object with same type to define a findFirst() class method
which returns Self? (the Swift equivalent of instancetype) without
being generic:
// Used to cast `AnyObject?` to `Self?`, `T` is inferred from the context.
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject
{
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString) { $0 == "." }
return components.last ?? classString
}
// Return any matching object, or `nil` if none exists or an error occurred
class func findFirst(context : NSManagedObjectContext, withPredicate pred : NSPredicate?) -> Self? {
let name = entityName()
let request = NSFetchRequest(entityName: name)
request.predicate = pred
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)
if let objects = result {
return objcast(objects.first)
} else {
println("Fetch failed: \(error?.localizedDescription)")
return nil
}
}
}
This can be used from Swift
if let obj = YourEntity.findFirst(context, withPredicate: nil) {
// found
} else {
// not found
}
and from Objective-C:
YourEntity *obj = [YourEntity findFirst:context withPredicate:nil];

Cast closures/blocks

In Objective-C, I often pass around blocks. I use them very often to implement patterns that help avoid storing stuff into instance variables, thus avoiding threading/timing issues.
For example, I assign them to a CAAnimation via -[CAAnimation setValue:forKey:] so I can execute the block when the animation is finished. (Objective-C can treat blocks as objects; you also can do [someBlock copy] and [someBlock release].)
However, trying to use these patterns in Swift together with Objective-C seems to be very difficult. (Edit: and we can see that the language is still in flux: have adapted the code so it works on Xcode6-beta2, previous version worked on Xcode6-beta1.)
For example, I can't convert AnyObject back to a block/closure. The following yields an error from the compiler:
override func animationDidStop(anim: CAAnimation!, finished flag: Bool)
{
let completion : AnyObject! = anim.valueForKey("completionClosure")
(completion as (#objc_block ()->Void))()
// Cannot convert the expression's type 'Void' to type '#objc_block () -> Void'
}
I have found a workaround, but it's pretty ugly, IMHO: in my bridging header, I have:
static inline id blockToObject(void(^block)())
{
return block;
}
static inline void callBlockAsObject(id block)
{
((void(^)())block)();
}
And now I can do this in Swift:
func someFunc(completion: (#objc_block ()->Void))
{
let animation = CAKeyframeAnimation(keyPath: "position")
animation.delegate = self
animation.setValue(blockToObject(completion), forKey: "completionClosure")
…
}
override func animationDidStop(anim: CAAnimation!, finished flag: Bool)
{
let completion : AnyObject! = anim.valueForKey("completionClosure")
callBlockAsObject(completion)
}
It works, but I'd need a new function for every block type that I'd like to use and I'm hacking around the compiler which can't be good either.
So is there a way to solve this in a pure Swift way?
How about a generic Block parameterized with the function type?
class Block<T> {
let f : T
init (_ f: T) { self.f = f }
}
Allocate one of these; it will be a subtype of AnyObject and thus be assignable into dictionaries and arrays. This doesn't seem too onerous especially with the trailing closure syntax. In use:
5> var b1 = Block<() -> ()> { print ("Blocked b1") }
b1: Block<() -> ()> = {
f = ...
}
6> b1.f()
Blocked b1
and another example where the Block type is inferred:
11> var ar = [Block { (x:Int) in print ("Block: \(x)") }]
ar: [Block<(Int) -> ()>] = 1 value {
[0] = {
f = ...
}
}
12> ar[0].f(111)
Block: 111
I like GoZoner's solution - wrap the block in a custom class - but since you asked for the actual "Swift way" to perform the cast between a block and an AnyObject, I'll just give the answer to that question: cast with unsafeBitCast. (I'm guessing that this is more or less the same as Bryan Chen's reinterpretCast, which no longer exists.)
So, in my own code:
typealias MyDownloaderCompletionHandler = #objc_block (NSURL!) -> ()
Note: in Swift 2, this would be:
typealias MyDownloaderCompletionHandler = #convention(block) (NSURL!) -> ()
Here's the cast in one direction:
// ... cast from block to AnyObject
let ch : MyDownloaderCompletionHandler = // a completion handler closure
let ch2 : AnyObject = unsafeBitCast(ch, AnyObject.self)
Here's the cast back in the other direction:
// ... cast from AnyObject to block
let ch = // the AnyObject
let ch2 = unsafeBitCast(ch, MyDownloaderCompletionHandler.self)
// and now we can call it
ch2(url)
Here's yet another solution, allowing us to cast to exchange values with Objective-C. It builds on GoZoner's idea of wrapping the function in a class; the difference is our class is an NSObject subclass, and can thus give the function Objective-C block memory management without any hackery, and can be directly used as an AnyObject and handed over to Objective-C:
typealias MyStringExpecter = (String) -> ()
class StringExpecterHolder : NSObject {
var f : MyStringExpecter! = nil
}
Here's how to use it to wrap a function and pass where an AnyObject is expected:
func f (s:String) {println(s)}
let holder = StringExpecterHolder()
holder.f = f
let lay = CALayer()
lay.setValue(holder, forKey:"myFunction")
And here's how to extract the function later and call it:
let holder2 = lay.valueForKey("myFunction") as StringExpecterHolder
holder2.f("testing")
All you need to do is use reinterpretCast to perform force cast.
(reinterpretCast(completion) as (#objc_block Void -> Void))()
from REPL
1> import Foundation
2> var block : #objc_block Void -> Void = { println("test")}
block: #objc_block Void -> Void =
3> var obj = reinterpretCast(block) as AnyObject // this is how to cast block to AnyObject given it have #objc_block attribute
obj: __NSMallocBlock__ = {}
4> var block2 = reinterpretCast(obj) as (#objc_block Void -> Void)
block2: (#objc_block Void -> Void) =
5> block2()
test
6>