I have a shared instance of a class in Swift that I'm using in Objective-C. I'm unable to create the shared instance and use the instance function. Here's my Swift code.
class VideoPlayerSignaler: NSObject {
static let sharedInstance = VideoPlayerSignaler()
let playerAction = Signal<PlayerAction>()
private override init() {
}
func firePlayerAction(action: PlayerAction) {
playerAction.fire(action)
}
}
Here's my Objective-C code.
VideoPlayerSignaler *signaler = [VideoPlayerSignaler sharedInstance];
// This is the line that is producing the issue.
// It's as if the signaler variable is a Class Instance
[signaler firePlayerAction: PlayerAction.Stop];
The error I'm producing states that firePlayerAction does not exist. In essence, Objective C believes the signaler variable to be a class instance.
What am I doing wrong and how do I fix it so that signaler is a shared instance of VideoPlayerSignaler?
There's nothing wrong with your Swift, or with how you access the singleton instance from ObjC — the problem is the enum value you're passing to it.
Presumably your enum declaration looks something like this:
enum PlayerAction: Int {
case Stop, Collaborate, Listen // etc
}
To make your enum accessible to ObjC, you need to preface the declaration with #objc:
#objc enum PlayerAction: Int { /*...*/ }
This makes it appear as a Cocoa-style NS_ENUM declaration in ObjC, creating global symbols for case names by concatenating the Swift enum type's name with the case names:
typedef NS_ENUM(NSInteger, PlayerAction) {
PlayerActionStop = 1,
PlayerActionCollaborate,
PlayerActionListen, // etc
};
So those names are what you should be passing when you call a method taking an enum value from ObjC:
[signaler firePlayerAction: PlayerActionStop]; // NOT PlayerAction.Stop
(The only docs I can find to cite for this are buried in the Attributes chapter in The Swift Programming Language — scroll down the to objc attribute.)
Related
In Swift 2, I was able to initialize an empty NSZone struct like so:
let z = NSZone() // ObjectiveC.NSZone
I used this to stub test calls that required NSZone. With Swift 3, the interface was changed so that the initializer is no longer available:
Swift 2.3:
public struct NSZone : NilLiteralConvertible {
public init()
/// Create an instance initialized with `nil`.
public init(nilLiteral: ())
}
Swift 3:
public struct NSZone {
}
Is Apple pushing to use an optional NSZone instead of the old NSZone() initializer which initialized NSZone with a nil value?
Try to extend NSZone to add an initializer results in an error:
extension NSZone {
init() { } // Return from initializer without initializing all stored properties
}
Per the Swift open source project's design doc for importing ObjC API into Swift 3:
Nullable NSZone parameters are given a default value of nil. Zones are essentially unused in Swift and should always be nil.
If you're testing any calls that require a zone, you should be passing nil anyway.
I want to access Swift code in Objective-c.
I have written one class in swift which contains static method. I want to access that Static Method in objective C Class.
Here is Class declaration:
#objc class LocalizedResource: NSObject {
/*!
* #discussion This function will get localize string for key
* #param key Localize key
* #return String for locaized key
* #code LocalizedResource.getStringForKey(key);
*/
static func getStringForKey(key:String) -> String {
let frameworkBundle = NSBundle.mainBundle()
let value = frameworkBundle.localizedStringForKey(key, value: nil, table: nil)
return value;
}
}
I have made following settings for it:
Product Module Name : MyProject
Defines Module : YES
Embedded Content Contains Swift : YES
Install Objective-C Compatibility Header : YES
Objective-C Bridging Header : $(SRCROOT)/MySources/SwiftBridgingHeader.h
Also I have added #Obj before my class declaration in Swift class.
I have import MyProject-Swift.h in the .m file where I want to access that method.
But when I am trying to access it, it is not allowing me to access that static method.
Is any one having solution for it? Is there something missing?
Thnaks.
Note in Swift 4 and the build setting swift 3 #objc inference is default, we must explicitly mark function as #objc, or it will not generate class function in Swift bridge header.
From your comment:
I am calling this method as follows:
errorMessage.text = LocalizedResource.getStringForKey(#"TIMED_OUT_ERROR");
In Objective-C, the "dot syntax" is used for properties,
not for methods. The correct call should be
errorMessage.text = [LocalizedResource getStringForKey:#"TIMED_OUT_ERROR"];
I want to lazy/inline implement a protocol in Swift.
So in the point of the implementation I will have access to variables outside the protocol scope ,
Same as implementing a interface in Java without declaring a class:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ?
func a0() {MyClass.count--}
func a1() {MyClass.count++}
}
someComponenet.delegate = a;
}
protocol SomeProtocol {
func a0()
func a1()
}
editing----
thanks i look at this solution, and i didn't see how to access a variable of the parent class.
all the examples show an Anonymous class but no one of the examples is accessing the parent variables .
What you're looking for is an inner class (not necessarily an anonymous one), declared in a scope that lets it access the count variable of a MyClass instance, and that adopts a protocol defined at a different scope. Right now Swift has a few of those pieces, but it doesn't look like you can put them all together in any way that's as concise as what you might be looking for.
You might think about declaring an inner class:
class MyView: UIView {
let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred
var count = 0 // type Int is inferred
class Helper: SomeProtocol {
func a0() { count-- } // ERROR
// ...
}
init() {
someComponent.delegate = Helper()
}
}
But that won't work, because count is implicitly self.count, where self is a Helper instance, not the MyView instance that "owns" the Helper instance. And there isn't a way to reference that MyView instance (or its properties) from within a Helper's methods, because you could just as well construct a MyView.Helper() without having an existing MyView instance. Inner classes (or nested types in general) in Swift nest only in lexical scope, not in existential ownership. (Or to put it another way, since you referenced Java: all inner classes in Swift are like static inner classes in Java. There's no non-static inner class.) If that's a feature you'd like, though, it's probably worth telling Apple you want it.
You could also try declaring Helper inside MyView.init() -- in Swift you can nest type definitions anywhere, including inside functions or methods of other types. Defined there, it can refer to MyView's properties. However, now the type information for Helper is only visible inside of MyView.init(), so when you assign it to someComponent.delegate (whose type is just SomeProtocol), you can't make use of it... this crashes the compiler, even. (That's another bug to report, but it's hard to say whether the bug is really "compiler crashes on valid usage" or "code is bad, but compiler crashes instead of producing error".)
The closest solution I can come up with looks something like this:
class SomeInnerComponent {
var delegate: SomeProtocol?
}
protocol SomeProtocol {
func a0()
func a1()
}
class MyClass {
var someComponent = SomeInnerComponent()
var count = 0
struct Helper: SomeProtocol {
var dec: () -> ()
var inc: () -> ()
func a0() { dec() }
func a1() { inc() }
}
init() {
someComponent.delegate = Helper(
dec: { self.count -= 1 }, // see note below
inc: { self.count += 1 }
)
}
}
How it works:
Helper is an inner struct (could be a class, but a struct is simpler)
It implements the a0 and a1 methods, satisfying the requirements of SomeProtocol
The implementations of a0 and a1 call through to the closures dec and inc, which are stored properties (aka instance variables) of the Helper struct
You write (or otherwise specify) these closures when you construct a Helper instance (using the default member-wise initializer, Helper(dec: (Void -> Void), inc: (Void -> Void)))
Because you can write the closures when initializing a Helper, those closures can capture variables where you're calling the initializer, including the implicit self that refers to the MyClass instance creating the Helper.
You need both a0/a1 and dec/inc because you need closures (the latter), not methods, for capturing the enclosing state. And even though closures and funcs/methods are in many ways interchangeable, you can't create a method/func implementation by assigning a closure to a method/func name. (It'd be a different story if SomeProtocol required closure properties instead of methods, but I'm assuming SomeProtocol isn't something under your control.)
Anyway, this is kind of a lot of boilerplate and a layer of abstraction that you might not really need, so it's probably worth looking into other ways to architect your code.
Note: my example uses the closure { self.count -= 1 } where you might expect { self.count-- }. The latter doesn't work because that's an expression with a value, so Swift will interpret it as shorthand for the closure's return value. Then it'll complain that you assigned a () -> Int closure to a property that expects a () -> () (aka Void -> Void) closure. Using -= 1 instead works around this issue.
I would go for a different approach, I know this a pretty old topic but just in case someone else struggles with this issue:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
init(){
// Assign the delegate or do it somewhere else to your preference:
someComponenet.delegate = ProtocolImplementation(myClass: self);
}
private class ProtocolImplementation: SomeProtocol {
let selfReference: MyClass
init(myClass: MyClass){
selfReference = myClass
}
public func a0(){
selfReference.count--
}
public func a1(){
selfReference.count++
}
}
}
protocol SomeProtocol {
func a0()
func a1()
}
By following this approach it's also possible to include the same protocol multiple times, lets say your Protocol supports a generic and you want to implement it twice. SomeProtocol< SomeObject > and SomeProtocol< OtherObject > could be both used this way if needed.
Kind regards
I have a typedef defined in my objective-c files:
typedef enum : long{
tb_closed_k = 0,
tb_open_k
} MyState;
Now I create a member of this type in my Swift class:
#objc class myClass {
var activeState: MyState = tb_closed_k
func testState( state:MyState ){
if state != activeState{
..do something
}
}
}
so the line comparing the states gives me the error "MyState is not convertible to 'NativeObject'. I am using xCode 6-Beta3. Any Ideas.
thanks
Reza
Your enum is not directly importable into Swift because it does not use the NS_ENUM macro. Either rewrite the enum in Swift (using Int, not long) or incorporate NS_ENUM. In any case, using long here is a really bad idea in any case, because it won't come into Swift as a native type, whereas NSInteger will come in as Int.
I'm looking for behavior similar to Objective-C's +(void)initialize class method, in that the method is called once when the class is initialized, and never again thereafter.
A simple class init () {} in a class closure would be really sleek! And obviously when we get to use "class vars" instead of "static vars in a struct closure", this will all match really well!
If you have an Objective-C class, it's easiest to just override +initialize. However, make sure subclasses of your class also override +initialize or else your class's +initialize may get called more than once! If you want, you can use dispatch_once() (mentioned below) to safeguard against multiple calls.
class MyView : UIView {
override class func initialize () {
// Do stuff
}
}
If you have a Swift class, the best you can get is dispatch_once() inside the init() statement.
private var once = dispatch_once_t()
class MyObject {
init () {
dispatch_once(&once) {
// Do stuff
}
}
}
This solution differs from +initialize (which is called the first time an Objective-C class is messaged) and thus isn't a true answer to the question. But it works good enough, IMO.
There is no type initializer in Swift.
“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
You could use a type property which default value is a closure. So the code in the closure would be executed when the type property (or class variable) is set.
class FirstClass {
class var someProperty = {
// you can init the class member with anything you like or perform any code
return SomeType
}()
}
But class stored properties not yet supported (tested in Xcode 8).
One answer is to use static, it is the same as class final.
Good link for that is
Setting a Default Property Value with a Closure or Function
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
Code example:
class FirstClass {
static let someProperty = {
() -> [Bool] in
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
print("setting default property value with a closure")
return temporaryBoard
}()
}
print("start")
FirstClass.someProperty
Prints
start
setting default property value with a closure
So it is lazy evaluated.
For #objc classes, class func initialize() definitely works, since +initialize is implemented by the Objective-C runtime. But for "native" Swift classes, you'll have to see the other answers.
You can use stored type properties instead of initialize method.
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
}
But since stored types properties are actually lazily initialized on their first access, you will need refer them somewhere. You can do this with ordinary stored property:
class SomeClass: {
private static let initializer: Void = {
//some initialization
}()
private let initializer: Void = SomeClass.initializer
}
#aleclarson nailed it, but as of recent Swift 4 you cannot directly override initialize. You still can achieve it with Objective-C and categories for classes inheriting from NSObject with a class / static swiftyInitialize method, which gets invoked from Objective-C in MyClass.m, which you include in compile sources alongside MyClass.swift:
# MyView.swift
import Foundation
public class MyView: UIView
{
#objc public static func swiftyInitialize() {
Swift.print("Rock 'n' roll!")
}
}
# MyView.m
#import "MyProject-Swift.h"
#implementation MyView (private)
+ (void)initialize { [self swiftyInitialize]; }
#end
If your class cannot inherit from NSObject and using +load instead of +initialize is a suitable fit, you can do something like this:
# MyClass.swift
import Foundation
public class MyClass
{
public static func load() {
Swift.print("Rock 'n' roll!")
}
}
public class MyClassObjC: NSObject
{
#objc public static func swiftyLoad() {
MyClass.load()
}
}
# MyClass.m
#import "MyProject-Swift.h"
#implementation MyClassObjC (private)
+ (void)load { [self swiftyLoad]; }
#end
There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
I can't find any valid use case to have something like +[initialize] in Swift. Maybe this explains way it does not exist
Why do we need +[initialize] in ObjC?
To initialize some global variable
static NSArray *array;
+ (void)initialize {
array = #[1,2,3];
}
which in Swift
struct Foo {
static let array = [1,2,3]
}
To do some hack
+ (void)initialize {
swizzle_methodImplementation()
}
which is not supported by Swift (I can't figure out how to do it for pure Swift class/struct/enum)