I am migrating code from Objective-C to Swift 4.0. Here I have some float #define constants related to my deviceHeight in Specific Objective-C header class. While accessing this #define giving error "Use of unresolved identifier". When I use Objective-C string #define identifier it's easily accessible within Swift class.
Not accessible in Swift4
#define PHONE_IPHONE10 PHONE_UISCREEN_HEIGHT==812.0f
Accessible in Swift4
#define ERROR #"Some error occured. Please try later."
Help me with your comments or solution.
The reason this imports to Swift...
#define ERROR #"Some error occured. Please try later."
...is that it’s semantically equivalent to a constant declaration. That is, it permanently associates that string-literal value with the name ERROR. The Swift compiler recognizes that you’re using the C preprocessor to define a constant, and translates it to a Swift constant.
(Even though you could—and probably should—define C global constants without the preprocessor, Swift recognizes that there’s a long tradition of using #define instead, and imports it anyway.)
The reason this doesn’t import to Swift...
#define PHONE_IPHONE10 PHONE_UISCREEN_HEIGHT==812.0f
...is that this is a preprocessor macro. It doesn’t statically map a name to a value. Instead, it tells C that wherever it sees your name PHONE_IPHONE10, it should substitute the expression PHONE_UISCREEN_HEIGHT==812.0f. Presumably PHONE_UISCREEN_HEIGHT is itself a macro, so the whole thing expands to a chain of method calls and an equality comparison.
Swift itself doesn’t do preprocessor macros, or anything like such, so it doesn’t import them from C.
A close equivalent would be to redefine this logic using a computed property or function (and the idiomatic way to do that in Swift would be as a static member on a type, not a global symbol). Something like this:
extension UIDevice {
class var isMaybeiPhoneX: Bool {
return false // or some logic based on UIScreen.main.size
}
}
But be warned, the whole idea of conditionally changing your app’s UI or behavior based on a specific screen height check is fraught with peril. Tried Auto Layout?
To achieve similar functionality I created Constants.swift file with this structure:
struct Constants {
struct phoneHeights {
static let PHONE_UISCREEN_HEIGHT = 812.0
//some others consts
}
struct iPhoneX {
static let statusBarHeight: CGFloat = 44
//some others consts
}
}
Or simply:
struct Constants {
static let PHONE_UISCREEN_HEIGHT = 812.0
static let statusBarHeight: CGFloat = 44
}
And for type safety in Swift, you can read here.
Related
i have an Obj-c class "constants.h" that looks like this:
// constants.h
#import <Foundation/Foundation.h>
#interface constants : NSObject
#define kLookAhead 3600*24*7*4 //seconds*hours*days*weeks
end
In my myProduct-Bridging-Header.h, I have:
#import "constants.h"
However, when I call kLookAhead in a Swift class, e.g. like this
let timeInterval = kLookAhead
I get the error message "Use of unresolved identifier 'kLookAhead'"
All other classes that I import in myProduct-Bridging-Header.h are working just fine, and kLookAhead is successfully used in my objc classes.
Is there something I should know?
Only "simple" macros are imported from (Objective-)C to Swift,
compare "Interacting with C APIs" in the
"Using Swift with Cocoa and Objective-C" reference.
For example,
#define kSimpleInt 1234
#define kSimpleFloat 12.34
#define kSimpleString "foo"
are imported to Swift as
public var kSimpleInt: Int32 { get }
public var kSimpleFloat: Double { get }
public var kSimpleString: String { get }
but
#define kLookAhead 3600*24*7*4
is not imported. A possible solution is to replace the macro
definition by a static constant variable:
static const int kLookAhead = 3600*24*7*4;
Remark: Note that a day does not necessarily have 24 hours, it can
be 23 or 25 hours when the clocks are adjusted back or forward
for daylight saving time. It is generally better to use DateComponents and Calendar methods for calendrical calculations instead of fixed
time intervals. Example:
let now = Date()
let fourWeeksAhead = Calendar.current.date(byAdding: .weekdayOrdinal, value: 4, to: now)
In Swift, simple macros are available through bridging header file. From Apple's documentation:
Where you typically used the #define directive to define a primitive
constant in C and Objective-C, in Swift you use a global constant
instead. For example, the constant definition #define
FADE_ANIMATION_DURATION 0.35 can be better expressed in Swift with let
FADE_ANIMATION_DURATION = 0.35. Because simple constant-like macros
map directly to Swift global variables, the compiler automatically
imports simple macros defined in C and Objective-C source files.
So, you are right in assuming that your macro should be available as a global constant. But, I guess all that multiplication had made it difficult for the compiler to see the macro as a simple one.
I used the result as a macro on a sample project and it worked
#define kLookAhead 2419200 //seconds*hours*days*weeks
let timeInterval = kLookAhead // worked
I am writing an OS X/iOS framework in Objective-C, and I would like for the framework to be useful for developers using either Objective-C or Swift.
In normal Objective-C enums are defined like this (this example is taken directly from Apple's own UIView class reference).
typedef enum {
UIViewAnimationCurveEaseInOut,
UIViewAnimationCurveEaseIn,
UIViewAnimationCurveEaseOut,
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
To make this enum Swift-friendly, my understanding is that it should be declared like this.
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurve_EaseInOut,
UIViewAnimationCurve_EaseIn,
UIViewAnimationCurve_EaseOut,
UIViewAnimationCurve_Linear
};
This allows the enum to be accessed in the style of let curve: UIViewAnimationCurve = .EaseInOut from Swift.
My problem is that the NS_ENUM and underscore method produces strangely named enums when used from Objective-C. The NS_ENUM method allows dot notation to be used from Swift, but it also means that any ObjC code will need to use an underscore in the enumerated name, which is undesirable.
How can I allow dot notation for Swift while still preserving Objective-C style naming conventions for within ObjC code?
You simply follow the usual convention – no underscoring is necessary. Swift compiler is smart enough to just cut the common prefix out (the part that matches the enum type name). You do have to use an NS_ENUM for the enum to be made visible to Swift, but it's good practice anyway.
Case in point, for instance UIViewAnimationCurve is defined in an Objective-C header in just the form you describe in your first code example and works just fine in Swift:
If you define it like this:
typedef long TrafficLightColor NS_TYPED_ENUM;
TrafficLightColor const TrafficLightColorRed;
TrafficLightColor const TrafficLightColorYellow;
TrafficLightColor const TrafficLightColorGreen;
if get compiled to swift like this:
struct TrafficLightColor: RawRepresentable, Equatable, Hashable {
typealias RawValue = Int
init(rawValue: RawValue)
var rawValue: RawValue { get }
static var red: TrafficLightColor { get }
static var yellow: TrafficLightColor { get }
static var green: TrafficLightColor { get }
}
Looks like what you need, anyway take a look at: https://itunes.apple.com/us/book/using-swift-with-cocoa-and-objective-c-swift-4-1-beta/id1002624212?mt=11
I am migrating a UIViewController class to train a bit with Swift. I am successfully using Objective-C code via the bridging header but I have the need of importing a constants file that contains #define directives.
I have seen in Using Swift with Cocoa and Objective-C (Simple macros) the following:
Simple Macros
Where you typically used the #define directive to define a primitive constant in C and Objective-C, in Swift you use a global constant instead. For example, the constant definition #define FADE_ANIMATION_DURATION 0.35 can be better expressed in Swift with let FADE_ANIMATION_DURATION = 0.35. Because simple constant-like macros map directly to Swift global variables, the compiler automatically imports simple macros defined in C and Objective-C source files.
So, it seems it's possible. I have imported the file containing my constants into the bridging header, but I have no visibility from my .swift file, cannot be resolved.
What should I do to make my constants visible to Swift?
UPDATE:
It seems working with NSString constants, but not with booleans:
#define kSTRING_CONSTANT #"a_string_constant" // resolved from swift
#define kBOOL_CONSTANT YES // unresolved from swift
At the moment, some #defines are converted and some aren't. More specifically:
#define A 1
...becomes:
var A: CInt { get }
Or:
#define B #"b"
...becomes:
var B: String { get }
Unfortunately, YES and NO aren't recognized and converted on the fly by the Swift compiler.
I suggest you convert your #defines to actual constants, which is better than #defines anyway.
.h:
extern NSString* const kSTRING_CONSTANT;
extern const BOOL kBOOL_CONSTANT;
.m
NSString* const kSTRING_CONSTANT = #"a_string_constant";
const BOOL kBOOL_CONSTANT = YES;
And then Swift will see:
var kSTRING_CONSTANT: NSString!
var kBOOL_CONSTANT: ObjCBool
Another option would be to change your BOOL defines to
#define kBOOL_CONSTANT 1
Faster. But not as good as actual constants.
Just a quick clarification on a few things from above.
Swift Constant are expressed using the keywordlet
For Example:
let kStringConstant:String = "a_string_constant"
Also, only in a protocol definition can you use { get }, example:
protocol MyExampleProtocol {
var B:String { get }
}
In swift you can declare an enum, variable or function outside of any class or function and it will be available in all your classes (globally)(without the need to import a specific file).
import Foundation
import MapKit
let kStringConstant:String = "monitoredRegions"
class UserLocationData : NSObject {
class func getAllMonitoredRegions()->[String]{
defaults.dictionaryForKey(kStringConstant)
}
simple swift language don't need an macros
all #define directives.
will be let
and complex macros should convert to be func
The alternative for macro can be global variable . We can declare global variable outside the class and access those without using class. Please find example below
import Foundation
let BASE_URL = "www.google.com"
class test {
}
I'm trying to convert some of my Obj-C class to Swift. And some other Obj-C classes still using enum in that converted class. I searched In the Pre-Release Docs and couldn't find it or maybe I missed it. Is there a way to use Swift enum in Obj-C Class? Or a link to the doc of this issue?
This is how I declared my enum in my old Obj-C code and new Swift code.
my old Obj-C Code:
typedef NS_ENUM(NSInteger, SomeEnum)
{
SomeEnumA,
SomeEnumB,
SomeEnumC
};
#interface SomeClass : NSObject
...
#end
my new Swift Code:
enum SomeEnum: NSInteger
{
case A
case B
case C
};
class SomeClass: NSObject
{
...
}
Update: From the answers. It can't be done in Swift older version than 1.2. But according to this official Swift Blog. In Swift 1.2 that released along with XCode 6.3, You can use Swift Enum in Objective-C by adding #objc in front of enum
As of Swift version 1.2 (Xcode 6.3) you can. Simply prefix the enum declaration with #objc
#objc enum Bear: Int {
case Black, Grizzly, Polar
}
Shamelessly taken from the Swift Blog
Note: This would not work for String enums or enums with associated values. Your enum will need to be Int-bound
In Objective-C this would look like
Bear type = BearBlack;
switch (type) {
case BearBlack:
case BearGrizzly:
case BearPolar:
[self runLikeHell];
}
To expand on the selected answer...
It is possible to share Swift style enums between Swift and Objective-C using NS_ENUM().
They just need to be defined in an Objective-C context using NS_ENUM() and they are made available using Swift dot notation.
From the Using Swift with Cocoa and Objective-C
Swift imports as a Swift enumeration any C-style enumeration marked with the NS_ENUM macro. This means that the prefixes to enumeration value names are truncated when they are imported into Swift, whether they’re defined in system frameworks or in custom code.
Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
Swift
let cellStyle: UITableViewCellStyle = .Default
From the Using Swift with Cocoa and Objective-C guide:
A Swift class or protocol must be marked with the #objc attribute to
be accessible and usable in Objective-C. [...]
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
So, no, you can't use a Swift enum in an Objective-C class.
Swift 4.1, Xcode 9.4.1:
1) Swift enum must be prefixed with #objc and be Int type:
// in .swift file:
#objc enum CalendarPermission: Int {
case authorized
case denied
case restricted
case undetermined
}
2) Objective-C name is enum name + case name, eg CalendarPermissionAuthorized:
// in .m file:
// point to something that returns the enum type (`CalendarPermission` here)
CalendarPermission calPermission = ...;
// use the enum values with their adjusted names
switch (calPermission) {
case CalendarPermissionAuthorized:
{
// code here
break;
}
case CalendarPermissionDenied:
case CalendarPermissionRestricted:
{
// code here
break;
}
case CalendarPermissionUndetermined:
{
// code here
break;
}
}
And, of course, remember to import your Swift bridging header as the last item in the Objective-C file's import list:
#import "MyAppViewController.h"
#import "MyApp-Swift.h"
If you prefer to keep ObjC codes as-they-are, you could add a helper header file in your project:
Swift2Objc_Helper.h
in the header file add this enum type:
typedef NS_ENUM(NSInteger, SomeEnum4ObjC)
{
SomeEnumA,
SomeEnumB
};
There may be another place in your .m file to make a change: to include the hidden header file:
#import "[YourProjectName]-Swift.h"
replace [YourProjectName] with your project name. This header file expose all Swift defined #objc classes, enums to ObjC.
You may get a warning message about implicit conversion from enumeration type... It is OK.
By the way, you could use this header helper file to keep some ObjC codes such as #define constants.
If you (like me) really want to make use of String enums, you could make a specialized interface for objective-c. For example:
enum Icon: String {
case HelpIcon
case StarIcon
...
}
// Make use of string enum when available:
public func addIcon(icon: Icon) {
...
}
// Fall back on strings when string enum not available (objective-c):
public func addIcon(iconName:String) {
addIcon(Icon(rawValue: iconName))
}
Of course, this will not give you the convenience of auto-complete (unless you define additional constants in the objective-c environment).
After researching this, I kept finding only partial answers, so I created an entire example of a Swift App bridged to Objective C that has Swift enums used by Objective C code and Objective C enums used by Swift code. It is a simple Xcode project that you can run and experiment with. It was written using Xcode 10.3 with Swift 5.0
Example Project
In case you are trying to observe an enum which looks like this:
enum EnumName: String {
case one = "One"
case two = "Two"
}
this workaround helped me.
Observable Class:
create #objc dynamic var observable: String?
create your enum instance like this:
private var _enumName: EnumName? {
didSet {
observable = _enumName!.rawValue
}
}
Observer Class:
create private var _enumName: EnumName?
create private let _instance = ObservableClass()
create
private var _enumObserver: NSKeyValueObservation = _instance.observe(\.observable, options: .new, changeHandler: { [weak self] (_, value) in
guard let newValue = value.newValue else { return }
self?._enumName = EnumName(rawValue: period)!
})
Than's it. Now each time you change the _enumName in the observable class, an appropriate instance on the observer class will be immediately updated as well.
This is of course an oversimplified implementation, but it should give you an idea of how to observe KVO-incompatible properties.
this might help a little more
Problem statement :- I have enum in swift class, which I am accessing form other swift classes, and Now I need to access it form my one of the objective C class.
Before accessing it from objective-c class :-
enum NTCType {
case RETRYNOW
case RETRYAFTER
}
var viewType: NTCType?
Changes for accessing it from objective c class
#objc enum NTCType :Int {
case RETRYNOW
case RETRYAFTER
}
and add a function to pass it on the value
#objc func setNtc(view:NTCType) {
self.viewType = view; // assign value to the variable
}
I have a method, like so:
- (void) simpleMethod {
var = someValue;
}
I wanted to define a function which exists only within that method (I can do this in python for example). I tried to define it like a normal C function, like this...
- (void) simpleMethod {
var = someValue;
int times1k(int theVar) {
return theVar * 1000;
}
ivar = times1k(var);
}
But Xcode throws various errors. Is it possible to define a function within a method in Objective-C? And if so, how?
No, Objective-C follows the strict C rules on this sort of thing, so nested functions are not normally allowed. GCC allowed them via a language extension but this extension has not been carried over to Clang and the modern toolchain.
What you can do instead is use blocks, which are Objective-C's version of what Python (and most of the rest of the world) calls closures. The syntax is a little funky because of the desire to remain a superset of C, but your example would be:
- (void) simpleMethod {
var = someValue;
// if you have a bunch of these, you might like to typedef
// the block type
int (^times1k)(int) = ^(int theVar){
return theVar * 1000;
};
// blocks can be called just like functions
ivar = times1k(var);
}
Because that's a closure rather than a simple nested function there are some rules you'd need to follow for declaring variables if you wanted them not to be captured at their values when the declaration is passed over, but none that are relevant to your example because your block is purely functional. Also times1k is a variable that you can in theory pass about, subject to following some unusual rules about memory management (or letting the ARC compiler worry about them for you).
For a first introduction to blocks, I like Joachim Bengtsson's article.