Swift globals and global functions in objective c - objective-c

the documentation says:
Global constants defined in C and Objective-C source files are automatically imported by the Swift compiler as Swift global constants.
But it doesn't say anything about the other way around. I need to define a global swift constant and be able to see it one the objective c side like a global c constant. Like on the swift side define:
public let CARDS = ["card1", "card2"]
and see use it on the objective c side like
NSLog(#"Cards count: %d", [CARDS count])
What should I do? I've already imported the swift automatically generated header like:
#import "MyProject-Swift.h"
and in Xcode if I command-click on it, it takes me to the correct place in the swift code, but at compile time I get:
'User of undeclared Identifier CARDS'
on my objective c side.

Here is the document about it
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Global variables (including constants) are unaccessible from Objective-C.
Instead, you have to declare a class which has accessors for the global constants.
// Swift
public let CARDS = ["card1", "card2"]
#objc class AppConstant {
private init() {}
class func cards() -> [String] { return CARDS }
}
// Objective-C
NSArray *cards = [AppConstant cards];

Nice answer by #rintaro, but another alternative simple Swift answer for constants that can be used in both Swift and Objective-C:
#objcMembers
class MyConstants: NSObject {
static let kMyConstant1 = "ConstantValue1";
static let kMyConstant2 = "ConstantValue2";
static let CARDS = ["card1", "card2"]
}
You can access this on both Swift and Objective-C by:
MyConstants.kMyConstant1 // this will return "ConstantValue1"
MyConstants.CARDS // this will return array ["card1", "card2"]

Swift global functions (ie. swift top-level functions) cannot be accessed by objC. Period. End of story.
See above answer from rintaro, to wit...
"This excludes Swift-only features such as those listed here:
. . .
Top-level functions defined in Swift"
boom

Related

Invoke Swift closure in Objective-C via `id`

This question has been inspired by this one: Swift closure in array becomes nil in Objective-c, which I answered with a workaround using a wrapper class, but I'm still curious as to why one can't call, in Objective-C, a Swift closure passed via id. A simple example follows.
Objective-C code:
// In a header
typedef void (^EmptyBlock)();
#interface MyClassOC : NSObject
-(void)invoke:(id)blk;
#end
// In an implementation (.m) file
#implementation MyClassOC
-(void)invoke:(id)blk {
EmptyBlock emptyBlock = blk;
emptyBlock();
}
#end
No problem providing an Objective-C block:
EmptyBlock block = ^{ puts("In OC empty block..."); };
MyClassOC * myClassOC = [[MyClassOC alloc] init];
[myClassOC invoke:block];
However, the Objective-C code in invoke... can't call a Swift closure passed in via id:
let myClassOC = MyClassOC()
let myBlock : EmptyBlock = {
print("In Swift EmptyBlock...")
}
myClassOC.invoke(myBlock)
I end up with EXC_BAD_ACCESS on this line:
EmptyBlock emptyBlock = blk;
Any idea what's going on here?
The reason is probably the support introduced in Swift 3 for any Swift type to be represented as an opaque object of id type in Objective-C. Read Swift Value Types in Objective-C in this Apple blog post for details. In that you will see that Swift types are passed as _SwiftValue * when the parameter type is id, this is an opaque type.
The argued benefit of this approach is that Swift value types can be stored in Objective-C collections; however the disadvantage is that you cannot convert to an Objective-C compatible value on the Objective-C side. Debug your code and you'll see the block is being passed as a _SwiftValue * and not an Objective-C block type.
Declare the bak parameter to have EmptyBlock type and your code works.
HTH
Because Swift closures and Objective-C blocks are not the same things. Swift automatically converts a closure to an Objective-C block if they see that you are calling a method with an Objective-C block type, but it doesn't do that otherwise. And as CRD's answer mentions, in Swift 3 any Swift value can be represented as an Objective-C object, so even though it sees that it expects an Objective-C object, it still doesn't know you want an Objective-C block because a Swift closure can still be bridged to an opaque object.
The only way I figured out to pass it from Swift and work is something like:
myClassOC.invoke(myBlock as #convention(block) () -> Void)

How do I expose a Swift global function to Objective-C? [duplicate]

the documentation says:
Global constants defined in C and Objective-C source files are automatically imported by the Swift compiler as Swift global constants.
But it doesn't say anything about the other way around. I need to define a global swift constant and be able to see it one the objective c side like a global c constant. Like on the swift side define:
public let CARDS = ["card1", "card2"]
and see use it on the objective c side like
NSLog(#"Cards count: %d", [CARDS count])
What should I do? I've already imported the swift automatically generated header like:
#import "MyProject-Swift.h"
and in Xcode if I command-click on it, it takes me to the correct place in the swift code, but at compile time I get:
'User of undeclared Identifier CARDS'
on my objective c side.
Here is the document about it
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Global variables (including constants) are unaccessible from Objective-C.
Instead, you have to declare a class which has accessors for the global constants.
// Swift
public let CARDS = ["card1", "card2"]
#objc class AppConstant {
private init() {}
class func cards() -> [String] { return CARDS }
}
// Objective-C
NSArray *cards = [AppConstant cards];
Nice answer by #rintaro, but another alternative simple Swift answer for constants that can be used in both Swift and Objective-C:
#objcMembers
class MyConstants: NSObject {
static let kMyConstant1 = "ConstantValue1";
static let kMyConstant2 = "ConstantValue2";
static let CARDS = ["card1", "card2"]
}
You can access this on both Swift and Objective-C by:
MyConstants.kMyConstant1 // this will return "ConstantValue1"
MyConstants.CARDS // this will return array ["card1", "card2"]
Swift global functions (ie. swift top-level functions) cannot be accessed by objC. Period. End of story.
See above answer from rintaro, to wit...
"This excludes Swift-only features such as those listed here:
. . .
Top-level functions defined in Swift"
boom

Can Objective-C code call Swift class extensions?

I searched some posts, I think I cannot write an extension under Swift, and call it from Objective-C code, right?
#objc like attributes only support methods, class, protocols ?
You can write a Swift extension and use it in Objective-C code. Tested with Xcode 6.1.1.
All you need to do is:
create your extension in Swift (#objc annotation needed since Swift 4.0.3)
#import "ProjectTarget-Swift.h" in your Objective-C class (where "ProjectTarget" represents the XCode target the Swift extension is associated with)
call the methods from the Swift extension
I found out that in Swift 4.0 I had to add #objc in front of my extension keyword in order for the Swift extension methods to become visible by an instance of the Objc class I was extending.
In short:
File configuration setup:
CustomClass.h
CustomClass.m
CustomClassExtension.swift
In CustomClassExtension:
#objc extension CustomClass
{
func method1()
{
...
}
}
In my AppDelegate.m:
self.customClass = [[CustomClass alloc] init];
[self.customClass method1];
This solution works for Swift 2.2 and Swift 3. Note that only extensions for classes (not for structs or enums) will be accessible from Objective-C.
import UIKit
extension UIColor {
//Custom colours
class func otherEventColor() -> UIColor {
return UIColor(red:0.525, green:0.49, blue:0.929, alpha:1)
}
}
Then #import "ProductModuleName-Swift.h" in your ObjC file.
Swift 4
extension UIColor {
// As of Swift 4.0.3, the #objc annotation is needed if you want to use the extension in Objective-C files
#objc
class func otherEventColor() -> UIColor {
return UIColor(red:0.525, green:0.49, blue:0.929, alpha:1)
}
}
As covered in the other answers, importing the generated Swift header works in most cases.
An exception to this is when the category is defined on a bridged type (i.e. the extension is defined on String and not NSString). These categories will not automatically be bridged to their Objective-C counterparts. To get around this, you'll either need to use the Objective-C type (and cast the return value in your Swift code with as String) or define an extension for both the Swift and Objective-C types.
Import "#import "ProductModuleName-Swift.h" header in objective-c file and add #objc infront of your extentsion in swift file. It will working fine in swift 4.2 and swift 5
If think you have configured everything correctly (marked your Swift entities with #objcMembers or #objc, imported the bridging header "ProductModuleName-Swift.h" and so on) – but still getting the No visible #interface for 'FooClass' declares the selector 'fooSelector' error:
Check if you see your interfaces in the bridging header by ctrl + cmd clicking on it. If you don't, check out my answer to this question: Swift to Objective-C header does not contain Swift classes

How would you incorporate a Swift struct/enum into Objective-C?

Scenario: I've exposed the Objective-C file to Swift via the .h bridge; so I can launch the Objective-C via Storyboard from .Swift.
However,
I have some global enum & structs declared in Environment.Swift file that ObjC needs:
enum BarButtonItem:Int {
case cancel = 1
case back
case save
case activate
case upload
case share
}
Accessing enum within Swift:
#IBAction func navButtonAction(sender: UIBarButtonItem) {
if let buttonItem = BarButtonItem(rawValue: sender.tag) {
switch buttonItem {
case .save:
println("{(3.3)AccessBoundaries} Save.")
default:
println("")
}
}
self.dismissViewControllerAnimated(true, completion: nil)
}
I want to access this (and other data types) in an Objective-C file:
- (IBAction)barButtonAction:(UIBarButtonItem *)sender {
BarButtonItem....?
}
Xcode doesn't recognize this.
How do I expose environment data types (or can I?) defined in a .Swift file to an Objective-C file?
...and, is it possible to interpret a .Swift's struct in Objective-C?
There are a number of Swift features that can't be used in Objective-C. I posted an answer to a question the other day that asked something similar. I'll re-post the answer here:
According to Apple's docs on Swift and Objective-C compatibility:
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions

How to declare a constant in swift that can be used in objective c

if I declare the swift constant as a global constant like:
let a = "123"
but the a cannot be found in objective c.
How to solve this?
From Apple Doc:
You’ll have access to anything within a class or protocol that’s marked with the #objc attribute as long as it’s compatible with Objective-C. This excludes Swift-only features such as those listed here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Therefore its not possible to access global variables(Constants) or global functions defined in Swift.
Possible Solutions:
From the Apple Document Swift programming language, You can Declare Type Properties as
class var constant: Int = {
return 10
}()
But currently in Swift(beta-3) Type properties are not supported.
You can declare a Class function to get a constant value:
In Swift:
class func myConst() -> String {
return "Your constant"
}
Accessing from Objective-C:
NSString *constantValue = [ClassName myConst];
NSLog(#"%#", constantValue);
Swift code:
public class MyClass: NSObject {
public static let myConst = "aConst"
}
and then in Objective-C:
[MyClass myConst]
Isn't this working as well? As in this works for me.
Also this is somewhat shorter as creating a object first (alloc, init). Making a new function for every constant is... not pretty :/
Update for Swift 4
Because of the changes in Swift 4's Objective-C inference, you need to add the #objc annotation to the declared constant as well. The previous declaration then becomes:
#objcMembers
public class MyClass: NSObject {
public static let myConst = "aConst"
}
The calling Objective-C code remains the same.
Using #objcMembers makes all constants available (as if you'd write #objc before each constant), but I've had times where the compiler somehow wouldn't generate the corresponding ObjC code.
In those cases I'd suggest adding the #objc decorator before the constant as well.
I.e.: #objc public static let myConst = "aConst"
You should not have any problem by using let in Objective-C, next example was made with Xcode 7.2 :
MyClass.swift
import Foundation
import UIKit
#objc class MyClass : NSObject { // <== #objc AND NSObject ARE BOTH NECESSARY!!!
let my_color = UIColor( red:128/255,green:32/255,blue:64/255,alpha:1 ) // <== CONSTANT!!!
}
MyObjectiveC.m
#import "PROJECTNAME-Swift.h" // <== NECESSARY TO RECOGNIZE SWIFT CLASSES!!!
#interface MyObjectiveC ()
#end
#implementation MyObjectiveC
#synthesize tableview; // <== ANY UI OBJECT, JUST AS EXAMPLE!!!
- (void) viewDidLoad () {
MyClass * mc = [ [ MyClass alloc ] init ]; // <== INSTANTIATE SWIFT CLASS!!!
tableview.backgroundColor = mc.my_color; // <== USE THE CONSTANT!!!
}
#end
PROJECTNAME is the name of your Xcode project, as shown in Project Navigator.
In your swift class,
let constant: Float = -1
class YourClass: NSObject {
class func getMyConstant() -> Float {return constant}
...
}
Clean, build to let xcode prepare this method useable for obj-c.
Then at your obj-c class
if ([YourClass getMyConstant] != 0) {
...
}
First of all you need to know about the important of auto-generated Swift header file.
It is the one that will made the magic to transcribe the Swift code to be understandable from Objective-C.
This file is auto-generated by Xcode (do not look for it in your project).
The important of this file is to use the correct name, it can match with your target name, but, may not, it is the product module name. (Search for it in your project settings as "Product module")
You need to import this file on the Objective-C class that you want to use a Swift class and also the Swift class name of your Swift file.
#import <ProductModuleName-Swift.h>
#class MySwiftClassName;
My Swift class should have the prefix #objc and inherit from NSObject:
#objc class MySwiftClassName: NSObject {
let mySwiftVar = "123"
}
Then you can call your Swift variable from the Objective-C file:
MySwiftClassName *mySwiftClassO = [[MySwiftClassName alloc] init];
NSString *myVar = mySwiftClassO.mySwiftVar;
Make sure to clean and rebuild your project after each change to force regenerate this auto-generated file.
If your Swift header file was auto-generated correctly you can navigate to it by clicking over the import file name and check if all the code you need was properly transcribed.
In the following post you can find more detailed information about this. https://solidgeargroup.com/bridging-swift-objective-c
Classes func don't work. The only solution I have found out is this one:
class YourController: NSObject {
#objc static let shared = YourController()
private override init() { }
#objc class func sharedInstance() -> YourController {
return YourController.shared
}
#objc let terms = "Your-String-here"
And then on Obj-c file:
[[YourController sharedInstance].terms]