iOS - Not able to access Static method of swift class - ios

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"];

Related

Init an empty NSZone struct with Swift 3

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.

The use of singleton class in objective c and swift

I have a project implemented in both Objective C and Swift classes and I need to have global variables to be shared among these classes .
I have two variables currentUsername and currentUsernumber , I need to use these two in each view in the project, what is the best way to implement that ?
I have tried to implement singleton class and here is my code :
class curentUserSingleton {
static var instance: curentUserSingleton!
var currentUsername: String = ""
var currentUsernumber: String = ""
// SHARED INSTANCE
class func sharedInstance(Name : String , Number : String) -> curentUserSingleton {
self.instance = (self.instance ?? curentUserSingleton(uName: Name , uNumber: Number))
return self.instance
}
// METHODS
init(uName : String , uNumber : String) {
self.currentUsername = uName
self.currentUsernumber = uNumber
}}
But I don't know how to use this class safely in the OC and Swift and I am a little confused since I get declaration errors when I use the class in my code!
Is this the right way to write a singleton class and how to call it in both languages ?
I'd be inclined to do something like:
class User: NSObject {
static let sharedInstance = User()
var name: String?
var number: String?
}
Then you can set and retrieve name and number like so in Swift:
User.sharedInstance.name = "Foo"
User.sharedInstance.number = "42"
print(User.sharedInstance.name)
print(User.sharedInstance.number)
Obviously, to access this from Objective-C .m file, you have to have to include the system generated header like so:
#import "ModuleName-Swift.h" // obviously, replace `ModuleName` with the name of your module
But then the syntax for setting and retrieving these properties is similar as it was in Swift:
[User sharedInstance].name = #"Foo";
[User sharedInstance].number = #"42";
NSLog(#"%#", [User sharedInstance].name);
NSLog(#"%#", [User sharedInstance].number);
To me it seems you do not need a singleton at all. I suggest you would be best of redesigning the architecture to have a user class that can store the information you are needing (and more if you finds the need in the future).
Then you could either pass that user object around between the view controllers as they need or perhaps easier define a currentUser property for the app delegate class.
That way each view controller can obtain the app delegate from the NSApp global reference to the application object and then get the current user object from there. With this pattern the app delegate acts as the globally accessible singleton you need without any need to manage it yourself.

Using Swift Shared Instance in Objective C

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

Class initializer in Swift?

In Java I can create a static initializer like:
static { ... }
In Swift I can have:
class MyClass {
class var myVar:Int?
}
Is it possible to create some kind of class/static var initializer in Swift?
If you need a computed property accessible from the class type and you want it to be like a constant value, the best option is static keyword.
Type Property Syntax
“For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation.” Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/pt/jEUH0.l
With class keyword a subclass can override the computed value.
Best solution:
class MyClass {
static var myVar: Int {
return 0
}
}

"initialize" class method for classes in Swift?

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)

Resources