115

Simply I have a struct that stores the application constants as below:

struct Constant {

    static let ParseApplicationId = "xxx"
    static let ParseClientKey = "xxx"

    static var AppGreenColor: UIColor {
        return UIColor(hexString: "67B632")
    }
}

These constants can be use in Swift code by calling Constant.ParseClientKey for example. But in my code, it also contains some Objective-C classes. So my question is how to use these constants in the Objective-C code?

If this way to declare constants is not good then what is the best way to create global constants to be used in both Swift and Objective-C code?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dinh Quan
  • 1,257
  • 2
  • 11
  • 10
  • 30
    Please follow common swift code style and use a lowercase letter to start your let/var identifiers. – Nikolai Ruhe Oct 03 '14 at 14:37
  • 5
    @NikolaiRuhe Would this not be the correct style for static properties of a struct? Much like `UIControlEvents.TouchUpInside`? – Luke Rogers Aug 24 '15 at 11:38
  • @LukeRogers `.TouchUpInside ` is an enumeration member, not a property of a struct. – Nikolai Ruhe Aug 24 '15 at 15:42
  • 1
    @NikolaiRuhe Take a look at the Swift declaration: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIControl_Class/#//apple_ref/c/tdef/UIControlEvents — it's definitely a struct. – Luke Rogers Aug 25 '15 at 10:01
  • 1
    @LukeRogers That's because of Swift 2.0's changed way of how to import `NS_OPTIONS` style enums. Semantically `UIControlEvent` is still an enumeration type. – Nikolai Ruhe Aug 25 '15 at 11:02
  • 9
    @NikolaiRuhe LukeRogers: Dinh didn't ask about that issue, and most of us viewers didn't click here to read about it. – original_username Oct 03 '16 at 21:23

4 Answers4

133

Sad to say, you can not expose struct, nor global variables to Objective-C. see the documentation, which states in part:

Use Classes When You Need Objective-C Interoperability

If you use an Objective-C API that needs to process your data, or you need to fit your data model into an existing class hierarchy defined in an Objective-C framework, you might need to use classes and class inheritance to model your data. For example, many Objective-C frameworks expose classes that you are expected to subclass.

As of now, IMHO, the best way is something like this:

let ParseApplicationId = "xxx"
let ParseClientKey = "xxx"
let AppGreenColor = UIColor(red: 0.2, green: 0.7, blue: 0.3 alpha: 1.0)

@objc class Constant: NSObject {
    private init() {}

    class func parseApplicationId() -> String { return ParseApplicationId }
    class func parseClientKey() -> String { return ParseClientKey }
    class func appGreenColor() -> UIColor { return AppGreenColor }
}

In Objective-C, you can use them like this:

NSString *appklicationId = [Constant parseApplicationId];
NSString *clientKey = [Constant parseClientKey];
UIColor *greenColor = [Constant appGreenColor];
qix
  • 7,228
  • 1
  • 55
  • 65
rintaro
  • 51,423
  • 14
  • 131
  • 139
  • Accessible to Swift also? – khunshan Nov 25 '14 at 08:03
  • @khunshan Yes. Accessible directly `ParseClientKey`, or via the class `Constant.clientKey()` – Fabian Dec 04 '14 at 21:20
  • 7
    Should use ```@objc class Constant: NSObject``` – William Hu Nov 05 '15 at 08:27
  • Amazing! Thank you. You will have to inherit from NSObject if you want to use "@objc" i.e. @objc class Constant: NSObject { ... } – Henry Heleine Jan 21 '16 at 14:09
  • unfortunately, using @objc class Constant: NSObject { ... } will render `private init()` useless in Objective-C, since you can't limit the scope of inherited functions. – pxpgraphics Jun 24 '16 at 19:20
  • It could not find out `Constant` class. So i need must add `#import "(ProjectName)-Swift.h"` into `.m` file. See more here http://stackoverflow.com/questions/24078043/call-swift-function-from-objective-c-class – Quang Tran Mar 06 '17 at 04:56
  • 2
    This works but in Swift 4, I also needed to put `@objc` in front of every `class func` to be able to call them from Objective C code. – ElectroBuddha Mar 20 '18 at 08:39
  • 2
    @ElectroBuddha You can do ```@objcMembers``` on the class to reveal the whole class to Objective-C code. – user1898712 Jun 26 '19 at 08:07
27

Why not create a file with both a struct and an @objc class, something like this:

import UIKit

extension UIColor {
    convenience init(hex: Int) {
        let components = (
            R: CGFloat((hex >> 16) & 0xff) / 255,
            G: CGFloat((hex >> 08) & 0xff) / 255,
            B: CGFloat((hex >> 00) & 0xff) / 255
        )
        self.init(red: components.R, green: components.G, blue: components.B, alpha: 1)
    }
}

extension CGColor {
    class func colorWithHex(hex: Int) -> CGColorRef {
        return UIColor(hex: hex).CGColor
    }
}

struct Constant {
    static let kParseApplicationId = "5678"
    static let kParseClientKey = "1234"
    static var kAppGreenColor: UIColor { return UIColor(hex:0x67B632) }
    static var kTextBlackColor: UIColor { return UIColor(hex:0x000000) }
    static var kSomeBgBlueColor: UIColor { return UIColor(hex:0x0000FF) }
    static var kLineGrayCGColor: CGColor { return CGColor.colorWithHex(0xCCCCCC) }
    static var kLineRedCGColor: CGColor { return CGColor.colorWithHex(0xFF0000) }
}


@objc class Constants: NSObject {
    private override init() {}

    class func parseApplicationId() -> String { return Constant.kParseApplicationId }
    class func parseClientKey() -> String { return Constant.kParseClientKey }
    class func appGreenColor() -> UIColor { return Constant.kAppGreenColor }
    class func textBlackColor() -> UIColor { return Constant.kTextBlackColor }
    class func someBgBlueColor() -> UIColor { return Constant.kSomeBgBlueColor }
    class func lineGrayCGColor() -> CGColor { return Constant.kLineGrayCGColor }
    class func lineRedCGColor() -> CGColor { return Constant.kLineRedCGColor }
}

For use in Objective-C files add this when you need to use constants:

#import "ProjectModuleName-Swift.h"

Swift usage:

self.view.backgroundColor = Constant.kAppGreenColor

Objective-C usage:

self.view.backgroundColor = [Constants appGreenColor];

This way you can update colors, default text, web service urls for whole app in one place.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Marijan V.
  • 403
  • 5
  • 6
  • 2
    It wasn't immediately clear to me that we could scroll your answer to discover the objective-c part! – Cœur Apr 04 '19 at 02:46
3

Though this might be late or redundant, I could make it work with the following code :

@objcMembers class Flags: NSObject {
    static let abcEnabled = false
    static let pqrEnabled = false
    .
    .
    .
}

Obviously, to use in objc c code, you have to do #import "ProjectModuleName-Swift.h"

Anil Arigela
  • 436
  • 2
  • 8
1

You should make the let statements private if you want to make other Swift types in your code to access these constants only via class:

private let AppGreenColor = UIColor(red: 0.2, green: 0.7, blue: 0.3 alpha: 1.0)

@objc class Constant {
    class func appGreenColor() -> UIColor { return AppGreenColor }
}

In Swift, you can use them like this:

UIColor *greenColor = Constant.appGreenColor

The following line will not compile anymore now since the let statement is private:

UIColor *greenColor = appGreenColor
koira
  • 1,197
  • 8
  • 10