Is it possible to call Swift convenience initializer in Objective-C - objective-c

Say I have a convenience initializer in Swift:
extension UIImage {
convenience init?(bundleNamed name: String) {
let bundle = NSBundle(forClass: Foo.self)
self.init(named: name, inBundle: bundle, compatibleWithTraitCollection: nil)
}
}
How might I call this in Objective-C? The following doesn't work:
[UIImage bundleNamed:#"Bar"];
[[UIImage alloc] initWithBundleNamed:#"Bar"];
Do I need an additional class extension solely for Objective-C?
Solution: following Lasse's answer below, the steps I had to do were:
In the Objective-C classes implementation file, add
#import <ModuleName-Swift.h>
then I had to delete derived data and rebuild. Then I was able to use the convenience initializer as follows:
[[UIImage alloc] initWithBundleNamed: #"Bar"];
I didn't need to declare the initializer as public because the default level of internal was sufficient for my module.

Check out Using Swift with Cocoa and Objective-C (Swift 2.2) - Mix and Match. What it seems to come down to is
Making your convenience initializer public, and
Importing an XCode-generated header file [YourProductModuleName]-Swift.h into your code

Yes we can use it Note: #objc and public are important
#objc public init(url: URL) {
//your code
}

Please note! If you are using any of Swift features that are not available in Objective-C (like Optional values), it would not be accessible in Objective-C.
So fix them.
public convenience init(title:String?, message:String?) {
self.init()
self.title = title
self.message = message
}
Above code is not accessible, so removing optional will help in this case.

Related

Convert Swift 2 closure to Objective-C block

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()

Type Recognition Issue in the process of translating OC to Swift

My Situation:
I have learnt Swift for a while and Swift is my only language I've learnt for iOS Development, which means I did not learn Objective-C Systematically. I can just read OC Code a little.
And I got a Type Recognition problem when I tried to translate a OC project to Swift project.
Objective-C Code:
static inline NSRegularExpression * AccountRegularExpression() {
static NSRegularExpression *_accountRegularExpression = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_accountRegularExpression = [[NSRegularExpression alloc] initWithPattern:AccountRegular options:NSRegularExpressionCaseInsensitive error:nil];
});
return _accountRegularExpression;
}
I confused with those mysterious code.
My Problem:
For now, I just met two different things in swift and OC, almost are variable instance and function, which means I can read and write the code to initial a variable instance and crate the function. But I never seen the thing like that in OC (Yep, I am a beginner...), it is not look like variable instance or method.
Before asked the question here, I tried to write a swift function to serve as the part like the original one. The thing is, complier did not thrown any error after I built the project.
Here is my Swift Code:
func AccountRegularExpression() -> NSRegularExpression {
var accountRegularExpression: NSRegularExpression!
var onceToken: dispatch_once_t = 0
dispatch_once(&onceToken, {
accountRegularExpression = NSRegularExpression(pattern: self.AccountRegular, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
})
return accountRegularExpression
}
I have no idea whether this Swift code have the same function like that OC code.
My Question:
Can you tell me is that OC code a variable instance? I think it's not a function or method.
I Google the key word, inline, which is the process for anti-compiling. Is this process still work in the Swift?
How to translate that OC code in Swift in the right way, I guess my swift code is not correct.
A big appreciation for your guide and time.
Ethan Joe
The translation is wrong because the variables _accountRegularExpression and onceToken are declared static in C, which for local variables means that its state is kept between function invocations, for the whole lifetime of the program, similar to global variables.
That they are static is essential for the correct functioning of this function, which is to return a singleton instance. If they are not static, then:
dispatch_once requires that you give it a pointer to a dispatch_once that is "stored in global or static scope". By giving it a local variable with automatic storage, you are invoking undefined behavior. It's impossible to know whether it will execute it once or more than once.
If _accountRegularExpression is not static, then that means this function cannot remember and return the same instance that it created before. Therefore, it is not a "singleton". If the dispatch_once only executes once, then on the second and subsequent time this function will return nil. If the dispatch_once executes more than once, then every time it executes again it will return a new independent instance. Under no situation does it return an instance that has been returned before.
To make it function correctly in Swift, one way is to make those variables global:
var accountRegularExpression: NSRegularExpression!
var onceToken: dispatch_once_t = 0
func AccountRegularExpression() -> NSRegularExpression {
dispatch_once(&onceToken, {
accountRegularExpression = NSRegularExpression(pattern: self.AccountRegular, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
})
return accountRegularExpression
}
However, a better way for the function to return a singleton in Swift would be something like:
func AccountRegularExpression() -> NSRegularExpression {
struct Singleton {
static let sharedInstance = NSRegularExpression(pattern: self.AccountRegular, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
}
return Singleton.sharedInstance
}
Or just get rid of the function and make it a computed property:
var accountRegularExpression : NSRegularExpression {
struct Singleton {
static let sharedInstance = NSRegularExpression(pattern: self.AccountRegular, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
}
return Singleton.sharedInstance
}
Or if this makes sense inside some class, then just put it directly as a static property of the class:
// inside some class
static let accountRegularExpression = NSRegularExpression(pattern: self.AccountRegular, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
TL;DR: It looks right, it is a function taking an argument of a function.
You are confused by block syntax in Objective-C, and closure (basically the same thing) syntax in Swift. Read the function / closure section in the swift language docs, and refer to this pages:
http://fuckingblocksyntax.com
http://fuckingclosuresyntax.com
A possibly correct translation would be-
class SomeClass
{
static let regularPatternForRegularAccount = "Some Pattern";
static let regularExpressionForRegularAccount = NSRegularExpression(pattern:SomeClass.regularPatternForRegularAccount,
options: NSRegularExpressionOptions.CaseInsensitive,
error: nil)
}
Here, we have made the regular expression a 'static let' and it will be evaluated lazily and only once for the first time it is accessed. Also look at:
singletons in swift

swift init not visible in objective-C

I'm trying to create init functions in Swift and create instances from Objective-C. The problem is that I don't see it in Project-Swift.h file and I'm not able to find the function while initializing. I have a function defined as below:
public init(userId: Int!) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId
}
I even tried putting #objc(initWithUserId:) and I keep getting the same error again. Is there anything else I'm missing? How do I get the constructor visible to Objective-C code?
I read the below for this:
https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/Initialization.html
https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/interactingwithobjective-capis.html
How to write Init method in Swift
How to define optional methods in Swift protocol?
The issue you're seeing is that Swift can't bridge optional value types -- Int is a value type, so Int! can't be bridged. Optional reference types (i.e., any class) bridge correctly, since they can always be nil in Objective-C. Your two options are to make the parameter non-optional, in which case it would be bridged to ObjC as an int or NSInteger:
// Swift
public init(userId: Int) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId
}
// ObjC
MyClass *instance = [[MyClass alloc] initWithUserId: 10];
Or use an optional NSNumber?, since that can be bridged as an optional value:
// Swift
public init(userId: NSNumber?) {
self.init(style: UITableViewStyle.Plain)
self.userId = userId?.integerValue
}
// ObjC
MyClass *instance = [[MyClass alloc] initWithUserId: #10]; // note the #-literal
Note, however, you're not actually treating the parameter like an optional - unless self.userId is also an optional you're setting yourself up for potential runtime crashes this way.
use this one:
var index: NSInteger!
#objc convenience init(index: NSInteger) {
self.init()
self.index = index
}

Swift Inheritance Issue - Cannot access inherited properties

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...

Generate an Obj-C Method

Is it possible to generate an Obj-C method? - For example:
- (void)doSomething:(BOOL)isLater
{
if (isLater == FALSE)
{
NSLog(#"Not Later");
} else {
NSLog(#"Is Later");
}
}
I'm specifically talking about generating the logic, I have some code that can create a class and add an existing method, but I'd like to know If I can generate the logic for the method itself and add it to a method?
static NSString *Description(id self, SEL _cmd)
{
return #"My Description Method";
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Class mySubclass = objc_allocateClassPair([NSObject class], "MySubclass", 0);
// grab NSObject's description signature so we can borrow it
Method description = class_getInstanceMethod([NSObject class],
#selector(description));
const char *types = method_getTypeEncoding(description);
// now add
class_addMethod(mySubclass, #selector(description), (IMP)Description, types);
objc_registerClassPair(mySubclass);
id myInstance = [[mySubclass alloc] init];
NSLog(#"%#", myInstance);
}
If we are splitting hair:
Yes, technically, you can.
But it won't be easy at all.
Objective-C can be parsed by libclang (the C++ library which the clang compiler is based on) and then JIT-compiled and run using LLVM.
This way, you can embed clang and LLVM into your program, generate some Objective-C source text dynamically, parse it into an abstract syntax tree (or generate that AST directly, without writing and parsing any Objective-C), and compiling it. If you write a method that does this, then you can essentially run arbitrary Objective-C code dynamically from within your application.