Im trying to import a swift class into my Obj C project. Everything is working but im trying to convert this Swift Closure statement to OBJ C. I believe it would be a Block in obj c but i just cant seem to get it, any help would be greatly appreciated !
Swift Declaration:
public var otpEnteredString :((String)->())?
Swift Use in View Controller:
ObjectName.otpEnteredString = { pin in
NSLog("The entered pin is %#",pin);
}
in Obj C im trying to do
[ObjectName otpEnteredString:^(NSString *pin){
NSLog(#"The entered pin is %#",pin);
}];
but i get the standard "No visible interface declares the selector ..
Any help would be greatly appreciated!
i can see the declaration in xcode but im not entirely sure how to translate that to obj c
Try this:
ObjectName.otpEnteredString = ^(NSString *pin){
NSLog(#"The entered pin is %#",pin);
};
Or this:
[ObjectName setOtpEnteredString:^(NSString *pin){
NSLog(#"The entered pin is %#",pin);
}];
Change to
#objc
public var optEnteredString : ((String) -> ())?
Related
I'm trying to build an Objective-C block in Swift 2 in order to add it to an NSArray like so :
typealias CompletionBlock = () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
nsArray.addObject(aBlock) // Error
I know it will work just fine with a Swift array, but I need an NSArray here for compatibility with existing Objective-C code. And if I use a swift array the compiler will refuse to cast it to an NSArray because it won't be a [AnyObject] (it will be a [Any]).
The problem here is that a swift closure is not an object contrary to Objective-C blocks which are objects behind the scene (they are instances of NSBlock which is a subclass of NSObject)
So my question is : How do a create an Objective-C block in swift ? I've tried using #convention (block) in the typealias but it doesn't work.
EDIT : As of Swift 3, this is completely unnecessary (and doesn't even work). Adding closures to Objective-C arrays works out of the box in Swift 3. The answer below is valid for Swift 2 only.
I know this is a duplicate but I will still post a refactored answer from swift-closure-as-anyobject and cast-closures-blocks in case anyone lands on this one first.
The solution is to use the unsafeBitCast function to convert the Swift closure to an Objective-C compatible object before adding it to an NSArray and back before using it in Swift.
// The `#convention(block)` is important here in order to get
// Objective-C like memory management
typealias CompletionBlock = #convention(block) () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
let blockObject = unsafeBitCast(aBlock, AnyObject.self)
nsArray.addObject(blockObject)
let closureObject = nsArray[0]
let closure = unsafeBitCast(closureObject, CompletionBlock.self)
closure()
I'm porting a Swift 1.2 framework to 2.0. After fixing the 3 million compiler errors courtesy of the new Swift 2.0 error handling scheme, I was finally able to link my test app (written in Objective C) to use the updated framework.
Xcode version 7.0 beta 3 (7A121l)
However, I ran into a problem. Some Swift functions are no longer generated into the automatically generated Objective C header (MyFramework-Swift.h) used by the Objective C test app.
Here is an example of a function that is not exposed: (My actual framework function returned an enum, but I tried to simplify to illustrate the problem more clearly).
public func calculateImportantValueDangerously() throws -> Int
{
return 42
}
Note that other functions like the following actually do get exposed as expected (and can be called):
public func doSomething()
{
}
public func doSomethingDangerous() throws
{
}
public func calculateMeaninglessValue() -> Int
{
return -1
}
Here's the Objective C side:
MyClass *newInstance = [[MyClass alloc] init];
[newInstance doSomething];
NSError *error = nil;
[newInstance doSomethingDangerousAndReturnError:&error];
long meaninglessValue = [newInstance calculateMeaninglessValue];
NSLog(#"%ld", meaninglessValue);
long importantValue = [newInstance calculateImportantValueDangerouslyAndReturnError:&error]; <-COMPILE ERROR
NSLog(#"%ld", importantValue);
From watching this video, I had expected that it should "just work":
https://developer.apple.com/videos/wwdc/2015/?id=401
...but it seems like we can't currently use functions that both throw and return a value.
Is this a bug, or not implemented feature? My apologies if I missed something in the release notes somewhere.
Any advice appreciated.
It is not possible.
If you annotate your method with #objc you will see the problem.
Throwing method cannot be marked #objc because it returns a value of
type 'Int'; return 'Void' or a type that bridges to an Objective-C
class
You can return only objects, primitives are not supported.
Although the selected answer is correct; you can't return an Int for a throwing function, I wanted to note an alternate solution since Apple has this same issue for a function in the CoreData framework:
func countForFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> Int
Note that in this case Apple is not using the throws pattern and instead reverting to the classic NSError out parameter mechanism.
I have a class in Obj-C, it can only be initialized by calling +new, and -init is not supported:
#interface SetupMainController : UIViewController
+(SetupMainController *)new;
-(id)init __attribute__((unavailable("Must use +new")));
#end
I am trying to run the following equivalent obj-c code in swift:
SetupMainController *setupController = [SetupMainController new];
[self presentViewController:setupController animated:YES completion:nil];
like so:
let sc : SparkSetupMainController = SparkSetupMainController.new()
or:
let sc : SparkSetupMainController.new()
or:
let sc : SparkSetupMainController()
(which obviously tries to call -init which is prohibited)
all fails,
getting "expected member name following '.'" error.
I found answers like this or this or apple docs but none give a straight answer how to do that simple task in Swift.
Help appriciated
If you change newin your Objective-C class to:
+(id)mynew;
Then suddenly all works:
var s: SetupMainController = SetupMainController.mynew()
Don't know why you cannot override +new. Maybe it's because is a class method?
EDIT: To add more mystery to this question, if you look up new in Apple's doc is defined in Swift using:
class func `new`() -> Self!
Do the backticks mean something like "this is reserved"?
EDIT 2: Looks like new doesn't work at all with Swift
This code does not compile:
let j = NSNumber.new()
var s = NSString.new()
EDIT 3:: SOLUTION
new is a reserved keyword in Swift, so to call +new() you need to add the back ticks:
let string = NSString.`new`()
Kudos to this answer
So I need to do this simple line of code in my game. It is currently in swift and I am trying to convert it to Objective-C and I got stuck as in Objective C there is no
sprite.frame.cointains(//CGPOINT);
I am wondering how can I go ahead and do this as it is vital for my game.
Here's the code in swift
if fruitNode.frame.contains(location!) {
touchPoint = location!
touching = true
}
If anybody can help me to respond that will be highly appreciated!
Thanks!
In Objective C you could do this using SKNode method:
if ([fruitNode containsPoint:location])
{
// do this
}
or if you needed to use CGRect for some reason :
if (CGRectContainsPoint(fruitNode.frame, location))
{
// do this
}
definitely check out the Apple reference for CGRectContainsPoint - https://developer.apple.com/library/prerelease/ios/documentation/GraphicsImaging/Reference/CGGeometry/index.html#//apple_ref/c/func/CGRectContainsPoint
I'm running into a strange issue that involves simple inheritance in Swift. I might be doing something totally stupid so if anyone has any advice.. thanks in advance!
I am using the latest XCode 6 GM version 6A313.
Here are the 2 Swift classes that's made Objective-C backward compatible.
#objc
public class ObjectA : NSObject {
var testProp: String!
init(testProp: String) {
self.testProp = testProp
}
}
#objc
public class ObjectB : ObjectA {
var testPropB: String!
init(testProp: String, testPropB: String) {
self.testPropB = testPropB
super.init(testProp: testProp)
}
}
I then initialize and use the object in Objective-C code.
ObjectB *objectB = [[ObjectB alloc] initWithTestProp: #"TestProp" testPropB: #"TestPropB"];
// This executes correctly and has the correct value
NSLog(#"%#", objectB.testPropB);
// I then pass this newly constructed object to another class that's written in Swift
AnotherClass *anotherClass = [[AnotherClass alloc] init];
[anotherClass someMethod:objectB];
Here is the Swift class where when I attempt to access the inherited property, I get an EXC_BAD_ACCESS code 1.
#objc
public class AnotherClass : NSObject {
public func someMethod(objectB: ObjectB) {
// This executes and assigns correctly
let prop = objectB.testProp
// This errors out with EXC_BAD_ACCESS error code 1
// In the debugger, objectB.testPropB actually seem to have the correct value
let propB = objectB.testPropB
}
}
var testProp: String!
There is no reason for this to be an implicitly unwrapped optional. You always initialize it in init. If possible, it should be:
let testProp: String
If it must be mutable after assignment, then it should be:
var testProp: String
Fixing this will probably cause the compiler to shout at you where you're doing something wrong (possibly not in the code you've posted here). With a !, it is legal to fail to initialize this value (you'll just crash later). Without a !, the compiler will better verify that you do initialize this value.
This line:
ObjectB *objectB = [[ObjectB alloc] initWithTestProp: #"TestProp", testPropB: #"TestPropB"];
is not correct ObjC (there's an extra comma). I'll assume that it's a typo. I don't think that can result in a legal C comma-operator that would cause the wrong initializer to run. But maybe... it's at least worth checking if you actually do have a comma in there. (I'm pretty sure testPropB:#"..." isn't a legal expression, so this is a little far-fetched, but it would match your symptom).
So, I ended up creating a dummy project to test my theory in a more controlled environment. The fix is basically to ensure that ObjectA and ObjectB are in 2 separate Swift files. When they are in the same Swift file, the error occurs.
Here is the sample code for anyone interested.
https://github.com/MystKnight/swift-inheritance-test
I'm not sure why this is but for everyone out there, I guess limit your files to one class if you are using inheritance...