Convert Swift 2 closure to Objective-C block - objective-c

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

Related

Key-Value Observing (KVO) between Swift and Objective-C

I am building a ViewController in Swift to update when state changes occur from an Objective-C class. Others have written the ObjC; I am implementing the Swift view.
While conceptually aware of NSNotification, Delegates and Callbacks 1, I am looking to employ Key-Value Observing (KVO).
Apple has overview documentation of KVOs 2 and for Swift 3. Others have documented in Swift 4 and ObjC 5 seperately, but it is still not clear how to implement KVO with a mixed Swift/ObjC environment.
Unclear after reading through the previous StackOverflow discussion on this.
Is key-value observation (KVO) available in Swift?
It is understood that the Swift object is the Observer and the ObjC object to be the Observed. Also understood that the Observer inherits from NSObject.
I am looking to have the Swift ViewController, specifically the lastUpdate variable, to be updated each time the ObjC instance variable, lastRealTimeSynchronizeDate, changes.
And still uncertain how and to express the dependencies on the property I want to observe, where and how to create the global context variable and how the observer for the key path works.
Sample code attached below where KVO has yet to be implemented.
Any guidance would be appreciated. Thank you
SynchronizeMgr.m below:
- (void)synchronizeCompleted
{
// standard date formatting
NSDate *now = [NSDate date];
NSString *dateString = [NSDateFormatter localizedStringFromDate:now
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
// want Swift ViewController, lastUpdate, to be updated each time this ivar changes
_lastRealTimeSynchronizeDate = dateString;
}
SynchronizeStatus.swift below:
class SynchronizeStatusViewController: UIViewController {
#IBOutlet weak var lastUpdate: UILabel!
func pullDate() -> String {
// retrieving from ObjC, lastRealTimeSynchronizeDate via sharedManager
let m = SynchronizeMgr.sharedManager()
let d = m.lastRealTimeSynchronizeDate
if d == nil {
return "nil"
} else {
return d
}
}
override func viewDidLoad() {
super.viewDidLoad()
// looking to have this update whenever lastRealTimeSynchronizeDate in ObjC is updated
lastUpdate.text = pullDate()
}
}

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

Force an illegal typecast in Swift

There's a failure case in my Objective-C app where an (NSDate *) points to a CFString and crashes when a date method is called.
I'm writing a unit test in Swift to simulate this case but it seems impossible because of Swift's type safety. I've written a little Objective-C factory class to create the messed up object for now but I was wondering if anyone knows a way to force an illegal typecast in Swift. Like a version of this that doesn't fail at runtime:
obj.date = NSString() as! NSDate
as! will always do a runtime check, and unsafeDowncast() will do a runtime check in debug builds (but not release builds).
To always skip the runtime check, use unsafeBitCast():
let myString : NSString = "Hello"
let myDate : NSDate = unsafeBitCast(myString, NSDate.self)
NSLog("%#", myDate) // "Hello"
Cast up to AnyObject and now you can cast down to any class you like.
let s = "howdy"
let id = s as AnyObject
let crash = id as! NSDate

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