I have this enum with String values, which will be used to tell an API method that logs to a server what kind of serverity a message has. I'm using Swift 1.2, so enums can be mapped to Objective-C
#objc enum LogSeverity : String {
case Debug = "DEBUG"
case Info = "INFO"
case Warn = "WARN"
case Error = "ERROR"
}
I get the error
#objc enum raw type String is not an integer type
I haven't managed to find anywhere which says that only integers can be translated to Objective-C from Swift. Is this the case? If so, does anyone have any best-practice suggestion on how to make something like this available in Objective-C?
One of the solutions is to use the RawRepresentable protocol.
It's not ideal to have to write the init and rawValue methods but that allows you to use this enum as usual in both Swift and Objective-C.
#objc public enum LogSeverity: Int, RawRepresentable {
case debug
case info
case warn
case error
public typealias RawValue = String
public var rawValue: RawValue {
switch self {
case .debug:
return "DEBUG"
case .info:
return "INFO"
case .warn:
return "WARN"
case .error:
return "ERROR"
}
}
public init?(rawValue: RawValue) {
switch rawValue {
case "DEBUG":
self = .debug
case "INFO":
self = .info
case "WARN":
self = .warn
case "ERROR":
self = .error
default:
return nil
}
}
}
From the Xcode 6.3 release notes (emphasis added):
Swift Language Enhancements
...
Swift enums can now be exported to Objective-C using the #objc
attribute. #objc enums must declare an integer raw type, and cannot be
generic or use associated values. Because Objective-C enums are not
namespaced, enum cases are imported into Objective-C as the
concatenation of the enum name and case name.
Here's a solution that works.
#objc public enum ConnectivityStatus: Int {
case Wifi
case Mobile
case Ethernet
case Off
func name() -> String {
switch self {
case .Wifi: return "wifi"
case .Mobile: return "mobile"
case .Ethernet: return "ethernet"
case .Off: return "off"
}
}
}
Here is work around if you really want to achieve the goal. However, you can access the enum values in objects that Objective C accepts, not as actual enum values.
enum LogSeverity : String {
case Debug = "DEBUG"
case Info = "INFO"
case Warn = "WARN"
case Error = "ERROR"
private func string() -> String {
return self.rawValue
}
}
#objc
class LogSeverityBridge: NSObject {
class func Debug() -> NSString {
return LogSeverity.Debug.string()
}
class func Info() -> NSString {
return LogSeverity.Info.string()
}
class func Warn() -> NSString {
return LogSeverity.Warn.string()
}
class func Error() -> NSString {
return LogSeverity.Error.string()
}
}
To call :
NSString *debugRawValue = [LogSeverityBridge Debug]
If you don't mind to define the values in (Objective) C, you can use the NS_TYPED_ENUM macro to import constants in Swift.
For example:
.h file
typedef NSString *const ProgrammingLanguage NS_TYPED_ENUM;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageSwift;
FOUNDATION_EXPORT ProgrammingLanguage ProgrammingLanguageObjectiveC;
.m file
ProgrammingLanguage ProgrammingLanguageSwift = #"Swift";
ProgrammingLanguage ProgrammingLanguageObjectiveC = #"ObjectiveC";
In Swift, this is imported as a struct as such:
struct ProgrammingLanguage: RawRepresentable, Equatable, Hashable {
typealias RawValue = String
init(rawValue: RawValue)
var rawValue: RawValue { get }
static var swift: ProgrammingLanguage { get }
static var objectiveC: ProgrammingLanguage { get }
}
Although the type is not bridged as an enum, it feels very similar to one when using it in Swift code.
You can read more about this technique in Grouping Related Objective-C Constants
Code for Xcode 8, using the fact that Int works but other methods aren't exposed to Objective-C. This is pretty horrible as it stands...
class EnumSupport : NSObject {
class func textFor(logSeverity severity: LogSeverity) -> String {
return severity.text()
}
}
#objc public enum LogSeverity: Int {
case Debug
case Info
case Warn
case Error
func text() -> String {
switch self {
case .Debug: return "debug"
case .Info: return "info"
case .Warn: return "warn"
case .Error: return "error"
}
}
}
This is my use case:
I avoid hard-coded Strings whenever I can, so that I get compile warnings when I change something
I have a fixed list of String values coming from a back end, which can also be nil
Here's my solution that involves no hard-coded Strings at all, supports missing values, and can be used elegantly in both Swift and Obj-C:
#objc enum InventoryItemType: Int {
private enum StringInventoryItemType: String {
case vial
case syringe
case crystalloid
case bloodProduct
case supplies
}
case vial
case syringe
case crystalloid
case bloodProduct
case supplies
case unknown
static func fromString(_ string: String?) -> InventoryItemType {
guard let string = string else {
return .unknown
}
guard let stringType = StringInventoryItemType(rawValue: string) else {
return .unknown
}
switch stringType {
case .vial:
return .vial
case .syringe:
return .syringe
case .crystalloid:
return .crystalloid
case .bloodProduct:
return .bloodProduct
case .supplies:
return .supplies
}
}
var stringValue: String? {
switch self {
case .vial:
return StringInventoryItemType.vial.rawValue
case .syringe:
return StringInventoryItemType.syringe.rawValue
case .crystalloid:
return StringInventoryItemType.crystalloid.rawValue
case .bloodProduct:
return StringInventoryItemType.bloodProduct.rawValue
case .supplies:
return StringInventoryItemType.supplies.rawValue
case .unknown:
return nil
}
}
}
Here's what I came up with. In my case, this enum was in the context providing info for a specific class, ServiceProvider.
class ServiceProvider {
#objc enum FieldName : Int {
case CITY
case LATITUDE
case LONGITUDE
case NAME
case GRADE
case POSTAL_CODE
case STATE
case REVIEW_COUNT
case COORDINATES
var string: String {
return ServiceProvider.FieldNameToString(self)
}
}
class func FieldNameToString(fieldName:FieldName) -> String {
switch fieldName {
case .CITY: return "city"
case .LATITUDE: return "latitude"
case .LONGITUDE: return "longitude"
case .NAME: return "name"
case .GRADE: return "overallGrade"
case .POSTAL_CODE: return "postalCode"
case .STATE: return "state"
case .REVIEW_COUNT: return "reviewCount"
case .COORDINATES: return "coordinates"
}
}
}
From Swift, you can use .string on an enum (similar to .rawValue).
From Objective-C, you can use [ServiceProvider FieldNameToString:enumValue];
You can create an private Inner enum. The implementation is a bit repeatable, but clear and easy. 1 line rawValue, 2 lines init, which always look the same. The Inner has a method returning the "outer" equivalent, and vice-versa.
Has the added benefit that you can directly map the enum case to a String, unlike other answers here.
Please feel welcome to build on this answer if you know how to solve the repeatability problem with templates, I don't have time to mingle with it right now.
#objc enum MyEnum: NSInteger, RawRepresentable, Equatable {
case
option1,
option2,
option3
// MARK: RawRepresentable
var rawValue: String {
return toInner().rawValue
}
init?(rawValue: String) {
guard let value = Inner(rawValue: rawValue)?.toOuter() else { return nil }
self = value
}
// MARK: Obj-C support
private func toInner() -> Inner {
switch self {
case .option1: return .option1
case .option3: return .option3
case .option2: return .option2
}
}
private enum Inner: String {
case
option1 = "option_1",
option2 = "option_2",
option3 = "option_3"
func toOuter() -> MyEnum {
switch self {
case .option1: return .option1
case .option3: return .option3
case .option2: return .option2
}
}
}
}
I think #Remi 's answer crashes in some situations as I had this:
My error's screesshot. so I post my edition for #Remi 's answer:
#objc public enum LogSeverity: Int, RawRepresentable {
case debug
case info
case warn
case error
public typealias RawValue = String
public var rawValue: RawValue {
switch self {
case .debug:
return "DEBUG"
case .info:
return "INFO"
case .warn:
return "WARN"
case .error:
return "ERROR"
}
}
public init?(rawValue: RawValue) {
switch rawValue {
case "DEBUG":
self = .debug
case "INFO":
self = .info
case "WARN":
self = .warn
case "ERROR":
self = .error
default:
return nil
}
}
}
I would like to do something like that:
public enum MyEnum: String {
case tapOnSection(section: String) = "tap on \(section)"
}
var section: String = "funny section"
func someFuntion() {
print(MyEnum.tapOnSection(self.section))
}
Console: "tap on funny section"
Do you some if could I do something like that or some suggestions?
Thank you
You can add methods to enums:
public enum MyEnum {
case tapOnSection(section: String)
var description: String {
switch self {
case let tapOnSection(section: section):
return "tap on \(section)"
}
}
}
var funnySection: String = "funny section"
print(MyEnum.tapOnSection(funnySection).description)
An enum can't have both associated values (i.e. cases with arguments) and raw values. That's a limitation of the language at the moment.
The simplest way I can think of to approximate what you want is to create a computed property that replaces the raw value in some way. e.g. for your specific case you can do this:
enum MyEnum
{
case tapOnSection(section: String)
}
// Might as well implement CustomStringConvertible so you can do
// print(MyEnum.something) instead of print(MyEnum.something.description)
extension MyEnum: CustomStringConvertible
{
var description: String
{
switch self
{
case .tapOnSection(let section):
return "tap on \(section)"
// A switch case for each case in your enum
}
}
}
print(MyEnum.tapOnSection(section: "foo")) // prints "tap on foo"
Is it possible to test whether a specific enum type can be initialized by rawValue when switching on a string, instead of using if let?
static func getCurrency(from code: String) -> Currency? {
if let fiatCurrency = Fiat(rawValue: code) {
return fiatCurrency
} else if let cryptoCurrency = Blockchain(rawValue: code) {
return cryptoCurrency
} else {
return nil
}
}
This may be similar to type casting, where currency adheres to my Currency protocol:
switch currency {
case let fiatCurrency as Fiat:
return getFiatFormatting(for: value, fiatCurrency: fiatCurrency)
case let blockchain as Blockchain:
return getCryptoFormatting(for: value, blockchain: blockchain)
case let token as Token:
return getTokenFormatting(for: value, token: token)
default:
return nil
}
Thanks!
If I understand correctly what you want, you can use the nil coalescing operator instead of if let.
static func getCurrency(from code: String) -> Currency? {
return Fiat(rawValue: code) ?? Blockchain(rawValue: code)
}
You can add as many other possible enum initializations as you want. It'll evaluate in order and return the first that it's not nil. If all are nil, it returns nil. So, it has exactly the same behaviour as a series of if-let-else.
Let's say I have some JSON, like so:
{
"some-random-key": {
"timestamp": 1234123423
"type": "text",
"content": "Hello!"
},
"some-other-key": {
"timestamp": 21341412314
"type": "image",
"path": "/path/to/image.png"
}
}
This JSON represents two message objects. Here's how I would like to represent them (Swift 4):
class Message: Codable {
let timestamp: Int
// ...codable protocol methods...
}
class TextMessage: Message { // first message should map to this class
let content: String
// ...other methods, including overridden codable protocol methods...
}
class ImageMessage: Message { // second message should map to this class
let path: String
// ...other methods, including overridden codable protocol methods...
}
How can I use the "type" attribute in the JSON to tell Codable which subclass to initialize? My first instinct was to use an enum as an intermediary which would allow me to go between the string representation and the metatypes
enum MessageType: String {
case image = "image"
case text = "text"
func getType() -> Message.Type {
switch self {
case .text: return TextMessage.self
case .image: return ImageMessage.self
}
}
init(type: Message.Type) {
switch type {
case TextMessage.self: self = .text
case ImageMessage.self: self = .image
default: break
}
}
}
However, the init here causes a compiler error-
Expression pattern of type 'TextMessage.Type' cannot match values of type 'Message.Type'
Is there a canonical/accepted way for handling this situation? Why doesn't the init here compile when the function getType does?
I would go for this approach:
static func createMessage(with json: [String: Any]) -> Message? {
guard let typeString = json["type"] as? String,
let type = MessageType(rawValue: typeString) else { return nil }
switch type {
case .image: return ImageMessage(....
and so on
The "is" keyword checks if metatypes represent subclasses of another metatype. This code works:
init(type: Message.Type) {
switch type {
case is TextMessage.Type: self = .text
case is ImageMessage.Type: self = .text
}
}
I know it is possible to pass class type to a function in swift:
func setGeneric<T>(type: T.Type){ }
setGeneric(Int.self)
But how we can return type from function? Writing something like
func getGeneric<T>() -> T.Type {
return Int.self
}
gives compiler error "Int is not identical to T". So is it possible to return type from a swift function?
Edit
Some explanation. I have classes that are used for persistence (I'm using Realm) and I have classes that acts as wrappers around this classes. All wrappers inherits from RealmClassWrapper which needs to know what Realm class it actually wraps. So lets say I have this realm model:
class RealmTodo: RLMObject {
dynamic var title = ""
}
and my wrappers supper class looks like this:
class RealmClassWrapper {
private let backingModel: RLMObject
//...
func backingModelType<T>() -> T.Type{ fatalError("must be implemented") }
}
and actual wrapper:
class Todo: RealmClassWrapper {
//some other properties
func backingModelType<T>() -> T.Type{ return RealmTodo.self }
}
You can return any type you want.
func getTypeOfInt() -> Int.Type { return Int.self }
func getTypeOfBool() -> Bool.Type { return Bool.self }
If the type is not determined from arguments or if the return is constant, there is no need to introduce a generic T type.
It works when I modify your function like this:
func getGeneric<T>(object: T) -> T.Type {
return T.self
}
getGeneric(0) // Swift.Int
You can force the downcast (as!) as below
func getGeneric<T>() -> T.Type {
return Int.self as! T.Type
}
But out of the function scope, you need to indicate the returned type:
var t:Int.Type = getGeneric()
Yes, this is possible. The problem here is that you say your function returns a generic T.type, but you always return Int.type. Since T is not always an Int, the compiler raises an error.
If you don't want to specify the return type you can use AnyClass as it instead of a template parameter.
class A {}
class B {}
public enum ExampleEnum: String {
case a
case b
func asClass() -> AnyClass {
switch self {
case .a:
return A.self
case .b:
return B.self
}
}
}
let myGoal : AnyClass = ExampleEnum.a.asClass()
You can also avoid the final cast to AnyClass, but compiler will show you an error