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
Difference Between Dispatchsourcetimer, Timer and Asyncafter
Xcode Playgrounds Can't Access Swift Files in Sources Folder
Appearance Proxies/Ui_Appearance_Selector in Swift
How to Make Uitextfield Behave Like a Uisearchbar in Swift
Output Compile Durations for Swift Files
Environmentobject VS Singleton in Swiftui
How to Use Println in Swift to Format Number
How to Listen to Global Hotkeys With Swift in a Macos App
How to Bring Nswindow to Front and to the Current Space
Swift, How to Implement Hashable Protocol Based on Object Reference
Uitableview Scrolls to Top on Reload
Nsdatecomponents Weekofyear Returns Wrong Date
How to Create Uicollectionviewcell Programmatically
How to Disable User Interaction on Swiftui View
Scenedidload Being Called Twice
How to Access a Swift Enum Associated Value Outside of a Switch Statement