Determine which Enum in associated enum - ios

I've created an associated Enum, however i can't seem to figure out how to create an if else statement, which determine which. it does not seem to work, what i'm doing. What am i doing wrong? or is it not possible with associated enums.
enum Type {
case Cat(name: String, outDoor: Bool)
case Dog(name: String, activityLevel: Int)
}
class
class Person {
var type: Type?
}
Function
func checkType(object: Person) {
if object.type == .Cat {
}
}

You have to use a switch statement, unless you are using Swift 2.0 which has the new if case statement for this very purpose.
enum Type {
case Cat(name: String, outDoor: Bool)
case Dog(name: String, activityLevel: Int)
}
class Person {
var type: Type?
}
func checkType(obj:Person) {
if let type = obj.type {
if case .Cat(name:let n, outDoor:let o) = type {
print(n)
print(o)
}
}
}

Related

Enum with associated value does not conform to CaseIterable and throws error

The following enum works fine without any error.
enum EnumOptions: CaseIterable {
case none
case mild
case moderate
case severe
case unmeasurable
}
When I try to add an associated value to one of the cases, it throws the following error "Type 'EnumOptions' does not conform to protocol 'CaseIterable'. Do you want to add protocol stubs?"
enum OedemaOptions: CaseIterable {
case none
case mild
case moderate
case severe
case unmeasurable(Int)
}
After adding stubs,
enum OedemaOptions: CaseIterable {
typealias AllCases = <#type#>
case none
case mild
case moderate
case severe
case unmeasurable(Int)
What should be filled up in the placeholder to make the Enum conform to CaseIterable, since there is only 1 case with associated value and not all the cases?
Automatic synthesis does not work for enums with associated values. You need to provide a custom implementation of the allCases property. Try,
enum OedemaOptions: CaseIterable {
static var allCases: [OedemaOptions] {
return [.none, .mild, .moderate, .severe, .unmeasurable(-1)]
}
case none
case mild
case moderate
case severe
case unmeasurable(Int)
}
You forgot to account for all 18,446,744,073,709,551,616 Ints.
Also, each one is an Option, not an Options.
static var allCases: [OedemaOption] {
[.none, .mild, .moderate, .severe]
+ (.min...(.max)).map(unmeasurable)
}
Compiler doesn't support automatic synthesis for CaseIterable in case with associated value(s).
This is my alternative solution to CaseIterable
import Foundation
protocol CustomCaseIterable {
associatedtype AllCustomCases: Hashable
static var allCustomCases: [AllCustomCases: Self] { get }
}
extension CustomCaseIterable {
init(_ string: String?, defaultCase: Self) {
self = Self(string) ?? defaultCase
}
init?(_ string: String?) {
guard
let string = string,
let caseValue = Self.allCustomCases.first(where: { "\($0.0)" == string })?.value
else {
return nil
}
self = caseValue
}
}
Example
enum MyEnum {
case one(Bool)
case two(Int)
case three(String)
case four
}
extension MyEnum: CustomCaseIterable {
static var allCustomCases: [String : Self] = [
"one_true": .one(true),
"two_zero": .two(.zero),
"three_empty": .three(""),
"four": .four
]
}
for (key, _) in MyEnum.allCustomCases {
print(key)
}
Extending that concept to RawRepresentable
extension CustomCaseIterable where AllCustomCases: RawRepresentable {
init(_ string: String?, defaultCase: Self) {
self = Self(string) ?? defaultCase
}
init?(_ string: String?) {
guard
let string = string,
let caseValue = Self.allCustomCases.first(where: { "\($0.0.rawValue)" == string })?.value
else {
return nil
}
self = caseValue
}
}
Example
enum MapStyle {
case primary(border: Bool)
case secondary(border: Bool)
case placeholder
}
enum JSONMapStyle: String, CaseIterable {
case primary
case primaryWithBorder = "PRIMARY_WITH_BORDER"
case secondary
case secondaryWithBorder = "SECONDARY_WITH_BORDER"
case placeholder
}
extension MapStyle: CustomCaseIterable {
static var allCustomCases: [JSONMapStyle: MapStyle] = [
.primary: .primary(border: false),
.primaryWithBorder: .primary(border: true),
.secondary: .secondary(border: false),
.secondaryWithBorder: .secondary(border: true),
.placeholder: .placeholder
]
}
for (key, _) in MapStyle.allCustomCases {
print(key.rawValue)
}

Protocol conforming to Equatable for Diffing

I have a small chat app here.
I can have 2 types of messages:
- text
- video
I am using polymorphism while decoding the JSON like so:
import Foundation
enum MessageType: Int, Decodable {
case text
case video
}
protocol Message: Decodable {
static var type: MessageType { get }
var id: String { get }
var user: User { get}
var timestamp: String { get }
}
struct TextMessage: Message {
static var type: MessageType = .text
var id: String
var user: User
var timestamp: String
let text: String
}
struct VideoMessage: Message {
static var type: MessageType = .video
var id: String
var user: User
var timestamp: String
let text: String
let link: String
let poster: String
}
enum MessageWrapper: Decodable {
enum CodingKeys: String, CodingKey {
case type
}
case text(TextMessage)
case video(VideoMessage)
var item: Message {
switch self {
case .text(let item): return item
case .video(let item): return item
}
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let type = try values.decode(Int.self, forKey: .type)
switch type {
case MessageType.text.rawValue: self = .text(try TextMessage(from: decoder))
case MessageType.video.rawValue: self = .video(try VideoMessage(from: decoder))
default:
throw DecodingError.dataCorruptedError(forKey: .type,
in: values,
debugDescription: "Invalid type")
}
}
}
I am also using the MVVM approach like so:
struct ChatViewModel {
enum ViewModelType {
case loading
case text(TextMessageViewModel)
case video(VideoMessageViewModel)
case failure(ErrorViewModel)
}
enum State {
case initialized
case loading
case loaded([Message])
case failed(Error)
}
let state: State
let viewModels: [ViewModelType]
init(with state: State) {
self.state = state
switch state {
case .initialized:
viewModels = []
case .loading:
viewModels = [
.loading,
]
......
}
}
In order to be able to use a Diffing library like Differ, the ChatViewModel should conform to the Equatable protocol.
extension ChatViewModel: Equatable {
static func == (lhs: ChatViewModel, rhs: ChatViewModel) -> Bool {
return lhs.state == rhs.state
}
}
extension ChatViewModel.State: Equatable {
static func == (lhs: ChatViewModel.State, rhs: ChatViewModel.State) -> Bool {
switch (lhs, rhs) {
case (.initialized, .initialized): return true
case (.loading, .loading): return true
case let (.loaded(l), .loaded(r)): return l == r
case let (.failed(l), .failed(r)): return l.localizedDescription == r.localizedDescription
default: return false
}
}
}
The problem here is for the case let (.loaded(l), .loaded(r)): return l == r, Message, as a protocol, doesn't conform to Equatable.
Making it conform to Equatable like
protocol Message: Decodable, Equatable {
static var type: MessageType { get }
var id: String { get }
var user: User { get}
var timestamp: String { get }
}
produce an error Protocol 'Message' can only be used as a generic constraint because it has Self or associated type requirements for the MessageWrapper:
enum MessageWrapper: Decodable {
...
var item: Message { // Protocol 'Message' can only be used as a generic constraint because it has Self or associated type requirements
switch self {
case .text(let item): return item
case .video(let item): return item
}
}
...
}
Any idea or suggestion to have a clean way to solve this? I saw some post about Type Erasure but after some tests I am not sure that it is actually solving the problem.
You don't have to conform to Equatable in order to be able to use the == operator. You can just define an operator like that yourself, without conforming to Equatable.
For convenience's sake, I'll assume that TextMessage and VideoMessage already conforms to Equatable.
First, write a method that compares Messages:
func messageEqual(m1: Message, m2: Message) -> Bool {
if let textMessage1 = m1 as? TextMessage, let textMessage2 = m2 as? TextMessage {
return textMessage1 == textMessage2
}
if let videoMessage1 = m1 as? VideoMessage, let videoMessage2 = m2 as? VideoMessage {
return videoMessage1 == videoMessage2
}
return false
}
Then a the == operator for [Message]:
func ==(messages1: [Message], messages2: [Message]) -> Bool {
return messages1.count == messages2.count &&
zip(messages1, messages2).allSatisfy(messageEqual)
}
Now l == r should compile.

How to create enum that takes parameter?

This is my enum
enum urlLink: String {
case linkOne = "http://test.com/linkOne"
}
and it works well in most of my cases. However, I now want to create another link which will takes parameters and it looks like this
"http://test.com/:params/info"
is there a way that I can add string parameter to one of my enum cases so that I can have something like this for linkTwo
enum urlLink: String {
case linkOne = "http://test.com/linkOne"
case linkTwo(input: String) = "http://test.com/" + input + "info"
}
Thank you so much!
You can't add raw values to enums with associated values. You can, however add properties and methods to your enum, so you can do something like this:
enum urlLink: CustomStringConvertible {
case linkOne
case linkTwo(input: String)
var description: String {
switch self {
case .linkOne:
return "http://test.com/linkOne"
case .linkTwo(let input):
return "http://test.com/\(input)info"
}
}
}
You can also conform to RawRepresentable:
enum urlLink: RawRepresentable {
case linkOne
case linkTwo(input: String)
var rawValue: String {
switch self {
case .linkOne:
return "http://test.com/linkOne"
case .linkTwo(let input):
return "http://test.com/\(input)info"
}
}
init?(rawValue: String) {
if rawValue == "http://test.com/linkOne" {
self = .linkOne
return
} else {
self = // some regex logic to get the "input" part
return
}
return nil
}
typealias RawValue = String
}

Store a closure with generic type

I am writing tests for a structure that has a func with a closure whom in turn has a child parameter of Result<ModelProtocol>.
However, when I am writing the mock for my struct it refuses to store the closure thinking that <T> != <ModelProtocol>. In turn this is correct, since this is a generic type.
The error I am getting now is:
Playground execution failed:
error: Test.playground:51:49: error: cannot assign value of type '(Result<T>) -> Void' to type '((Result<ModelProtocol>) -> Void)?'
self.doSomethingCompletionHandler = completionHandler
^~~~~~~~~~~~~~~~~
Which is the problem because <T> actually is of type T: ModelProtocol.
How can I store the closure (completionHandler) so I can call it at a later point to run the closure manually (by the test).
This is an example of what my problem in a playground:
public enum Result<Value> {
case success(Value)
case failure(Error)
public var value: Value? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
public var error: Error? {
switch self {
case .success:
return nil
case .failure(let error):
return error
}
}
}
protocol ModelProtocol {
init(aString: String)
}
protocol AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping((Result<T>)->Void))
}
enum StructureError : Error {
case defaultError
}
struct StructureOne : AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
debugPrint("Doing something")
if let model = ExampleModel(aString: "Test") as? T {
completionHandler(.success(model))
} else {
completionHandler(.failure(StructureError.defaultError))
}
}
}
class StructureOneMock : AnImportantProtocol {
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<ModelProtocol>)->Void)? = nil
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<ModelProtocol>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
struct ExampleModel {
let someString: String
}
extension ExampleModel : ModelProtocol {
init(aString: String) {
self.someString = aString
}
}
I believe you have a contravariance error that isn't obvious because the variance is happening in the generic parameter.
Consider the following code:
class Cat {}
class Kitten: Cat {}
class Cougar: Cat {}
protocol CatDayCareProtocol {
func setRaiseFunction(raiseFunction: #escaping (Cat) -> Cougar)
}
class CatDayCare: CatDayCareProtocol {
func setRaiseFunction(raiseFunction: #escaping (Cat) -> Cougar) {
self.raiseFunction = raiseFunction
}
private var raiseFunction: ((Cat) -> Cougar)? = nil
func callCougerRaiseFunction() -> Cougar? {
let cougar = Cougar()
return raiseFunction?(cougar)
}
}
let catDayCare = CatDayCare()
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
return Cougar()
})
In this example, swift throws the following error:
error: Contravariance.playground:23:51: error: expected expression
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
This seems unintuitive as a kitten is a Cat, so wouldn't this be fine? Let's consider what happens if you tried to do the callCougerRaiseFunction(). It instantiates a cougar, which is a cat, and calls its raise function, which expects a cat so this is legal. However, if you pass a function expecting a kitten as a parameter, suddenly you are passing a cougar to a function that wants a kitten and it is sad.
Now for your example, you have
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: #escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
In this example, T is strictly just as or more specific than a ModelProtocol (as it could be any protocol that inherits from ModelProtocol), which I believe makes Result<T> as a function parameter contravariant with the data type Result<ModelProtocol>. It is just that the compiler isn't quite smart enough to know it is contravariance, but it does know the conversion isn't legal.
As for actually solving the problem, is it really necessary to use a generic? Why can't you just use:
protocol AnImportantProtocol {
func doSomething(firstParameter: String, completionHandler: #escaping((Result<ModelProtocol>)->Void))
}
You are probably better off using an associatedType constraint in this case, for example:
protocol AnImportantProtocol {
associatedtype MyType: ModelProtocol
func doSomething(firstParameter: String, completionHandler ((Result<MyType>)->Void)?)
}
Then you typealias in the implementations:
class StructureOneMock<T: ModelProtocol> : AnImportantProtocol {
typealias MyType = T
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<MyType>)->Void)? = nil
func doSomething(firstParameter: String, completionHandler: ((Result<MyType>) -> Void)?) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<MyType>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
You could skip the generic T in the implementations and specify a concrete type there.

Swift Mutation inside switch with associated values

I have these structs I want to mutate:
public struct CheckoutViewModel {
var sections: [Section]
var total: String
public struct Section {
var title: String
var description: String
var kind: Kind
var expandState: ExpandState
enum Kind {
case products([ProductOrderViewModel])
case shippingMode(SelectableArray<ShippingMode>)
case shippingTarget(SelectableArray<ShippingKind>)
case billingAddress(SelectableArray<Address>)
case payment(SelectableArray<PaymentProvider>)
case promoCode(String?)
case legalAdvice(userAccepted: Bool)
}
}
}
struct SelectableArray<T> {
var selectedIndex: Int?
let options: [T]
init(options: [T]) {
self.options = options
self.selectedIndex = nil
}
mutating func select(atIndex: Int) throws -> T {
guard atIndex < options.count else {
throw SelectableArrayError.outOfBoundsIndex
}
selectedIndex = atIndex
return options[atIndex]
}
var selectedElement: T? {
guard let selectedIndex = selectedIndex else { return nil }
return options[selectedIndex]
}
}
I want to use this mutating func select() method inside SelectableArray, I am calling it from a chain of mutating functions (because Sections is nested inside a struct)
extension CheckoutViewModel {
mutating func select(atIndexPath indexPath: IndexPath) {
sections[indexPath.section].select(atIndex: indexPath.row)
}
}
extension CheckoutViewModel.Section {
mutating func select(atIndex idx: Int) {
switch kind {
case .shippingMode(var modes):
do { _ = try modes.select(atIndex: idx) } catch { return }
default:
return nil
}
dump(self) // Here self hasn't changed
}
}
The problem is that the CheckoutViewModel struct is never mutated. I am guessing that switch is not a mutating function, so var modes inside that switch is non mutable and then it does not matter if the following functions mutate anything. The workaround I managed to do is this:
mutating func select(atIndex idx: Int) {
switch kind {
case .shippingMode(var modes):
do {
_ = try modes.select(atIndex: idx)
self.kind = .shippingMode(modes)
} catch { return }
default:
return
}
}
Do you have any oher solution to this problem? Is there any sort of mutating switch function that I can use?
According to The Swift Programming Language:
A switch case can bind the value or values it matches to temporary
constants or variables, for use in the body of the case. This behavior
is known as value binding, because the values are bound to temporary
constants or variables within the case’s body.
Changes to such a temporary variable (e.g. the modes variable) do not affect the contents of the enum that is being switched on (e.g. kind).
For your first approach to work you would indeed need a different sort of switch statement that creates a reference to the enum's associated value, allowing you to modify that value in place. Such a statement doesn't exist in Swift 3.0.1.

Resources