Swift [1,2] Conforms to Anyobject But [Enum.A, Enum.B] Does Not

Swift [1,2] conforms to AnyObject but [Enum.a, Enum.b] does not

AnyObject exists for compatibility with Objective-C. You can only put objects into an [AnyObject] array that Objective-C can interpret. Swift enums are not compatible with Objective-C, so you have to convert them to something that is.

var x: AnyObject = [0, 1] works because Swift automatically handles the translation of Int into the type NSNumber which Objective-C can handle. Unfortunately, there is no such automatic conversion for Swift enums, so you are left to do something like:

var y: AnyObject = [E.a.rawValue, E.b.rawValue]

This assumes that your enum has an underlying type that Objective-C can handle, like String or Int.

Another example of something that doesn't work is an optional.

var a: Int? = 17
var b: AnyObject = [a] // '[Int?]' is not convertible to 'AnyObject'

See Working with Cocoa Data Types for more information.

Make a Swift (2.1) enum conform to AnyObject

This is not correct:

public typealias RESTPostDictionary = [RESTPostDictionaryKey : AnyObject]

What you mean is this:

public typealias RESTPostDictionary = [String : AnyObject]

The enum is not the string. But it is the string that you intend to use as the key.

When you use the enum case as the key, take its rawValue:

postDictionary[RESTRequestDictionaryKey.Requests.rawValue] = requestDictionaries

Thus, for example, this is legal:

enum Keys : String {
case Key1 = "hey"
case Key2 = "ho"
case Key3 = "hey nonny no"
}

var d = [String:AnyObject]()
d[Keys.Key1.rawValue] = 1
d[Keys.Key2.rawValue] = 2

Return an enum with associated value where AnyObject is expected

I suppose you have to ask if an enum is really the best option here. But if it is, can you not just create your own wrapper?

enum MyEnum {
case MyCase(String)
}

class MyEnumWrapper {
var myEnum: MyEnum

init(_ myEnum: MyEnum) {
self.myEnum = myEnum
}
}

func takeAnyObject(a: AnyObject!) {

if let myEnumW = a as? MyEnumWrapper {
print(myEnumW.myEnum)
}

}

let tmp = MyEnumWrapper(.MyCase("Hello"))
takeAnyObject(tmp)

Why casting function type as AnyObject works

Behind the scenes as AnyObject converts the casted value to an Objective-C compatible one: Int's become NSNumber, array's become NSArray, and so on. Swift-only values get wrapped within opaque SwiftValue instances.

print(type(of: test2)) // __SwiftValue

This is why adding as AnyObject makes your code compile, as the right-hand of the operator is now an object.

Store Enum with associated values in NSUserDefaults (Swift)

Write your own archiver.

This converts the enum with the associated value(s) to a property list representation (a dictionary) which can be saved directly in user defaults and can be converted back to the enum type.

enum DeepLinkAction {
case StartPractice(lessonName:String)
case StartTest
case Letters(didComplete:Bool, iconURL:String)

var propertyListRepresentation : [String:AnyObject] {
switch self {
case .StartPractice(let lessonName) : return ["StartPractice" : lessonName]
case .StartTest : return ["StartTest" : ""]
case .Letters(let complete, let iconURL) : return ["Letters" : [complete, iconURL]]
}
}

init(propertyList: [String:AnyObject]) {
assert(!propertyList.isEmpty, "property list must contain one key")
let key = propertyList.keys.first!
let value = propertyList[key]!
switch key {
case "StartPractice": self = .StartPractice(lessonName: value as! String)
case "Letters":
let parameters = value as! [AnyObject]
self = .Letters(didComplete: parameters[0] as! Bool, iconURL: parameters[1] as! String)

default: self = .StartTest
}

}
}

let action = DeepLinkAction.Letters(didComplete: false, iconURL: "http://myServer.com/path")
let plist = action.propertyListRepresentation // ["Letters": [0, "http://myServer.com/path"]]

let action2 = DeepLinkAction(propertyList: plist)

Comparing between two errors

First, make sure that each kind of error that you are dealing with properly conforms to Equatable. Why are we using Equatable rather than ===? Because the error types value types. RxCocoaURLError is an enum for example. And === never works on structs, even if you cast to AnyObject, because two different objects will be created when you do. Example:

enum Foo {
case a(Int)
case b(String)
}

(Foo.a(1) as AnyObject) === (Foo.a(1) as AnyObject) // false

The next step is to make ErrorMessageEvent generic (kind of like Optional, or half of Result):

enum ErrorMessageEvent<Failure: Error> {
case hide
case show(error: Failure)

// this (tries to) changes the type of the error
func mapError<T: Error>(toType type: T.Type) -> ErrorMessageEvent<T>? {
switch self {
case .hide:
return .hide
case .show(error: let e):
return (e as? T).map(ErrorMessageEvent<T>.show)
}
}
}

// messages with an equatable error are also equatable
// the compiler will synthesise the implementation for us
extension ErrorMessageEvent : Equatable where Failure : Equatable {}

You can use ErrorMessageEvent<Error> for the type of observable for viewModel.errorMessageEvent. Then, when you want to compare errors in a test, you'd know which type of error you are comparing at that point, so you can do:

let error = ApiError(type: .serviceUnavailable, message: "ABC")

// ...

// I *think* it can infer the types. If not, write out the full "ErrorMessageEvent<ApiError>.show"
XCTAssertEqual(
.show(error: error),
actualErrorMessageEvent?.mapError(toType: ApiError.self))

At this point, you might have realised. This is just reinventing the wheel of Optional<T>. So rather than ErrorMessageEvent, you can also consider an using an optional. Your ErrorMessageEvent might be overengineering.

let error = ApiError(type: .serviceUnavailable, message: "ABC")
let viewModel = createViewModel(dataProvider: { _ in .error(error) })

var actualErrorMessage: ApiError?
// viewModel.errorMessageEvent would be an Observable<Error?>
viewModel.errorMessageEvent
.emit(onNext: { actualErrorMessage = $0 as? ApiError })
.disposed(by: disposeBag)

viewModel.getDataButtonTapped(id: 1)

XCTAssertEqual(error, actualErrorMessageEvent)

It doesn't have as descriptive names as show and hide (It uses none and some instead), so if that's what you care about, I can understand.



Related Topics



Leave a reply



Submit