How do I make an enum codable? [duplicate] - ios

This question already has answers here:
How do I make an enum Decodable in Swift?
(9 answers)
Closed 2 years ago.
I have a nested an enum inside a struct that I want to conform to Codable. How do I make the enum codable and therefore make the struct codable?
Here is an example of what I have:
struct Person: Codable {
var firstName: String
var lastName: String
var favoriteColor: Color
enum Color {
case blue, red, green, yellow, pink, purple
}
}
Then, I get two errors :
Type 'Person' does not conform to protocol 'Decodable'
Type 'Person' does not conform to protocol 'Encodable'
How can I fix this problem?
Edit
I have also tried conforming Color to Codable. Xcode adds these protocol stubs:
init(from decoder: Decoder) throws {
<#code#>
}
func encode(to encoder: Encoder) throws {
<#code#>
}
What would I do with this?

struct Person: Codable {
var firstName: String
var lastName: String
var favoriteColor: Color
}
enum Color: String, Codable {
case blue, red, green, yellow, pink, purple
}

Related

Add a dynamic variable to struct [duplicate]

This question already has answers here:
Have a variable with multiple types in Swift
(4 answers)
What is the Swift equivalent of a Typescript union type?
(2 answers)
Closed 14 days ago.
I have a 2 structs like so:
struct StructA: Codable {
let title: String?
let subtitle: String?
let myImage: URL?
}
struct StructB: Codable {
let title: String?
let subtitle: String?
let myImage: UIImage?
}
Now, in StructA, the variable myImage is of type URL? and in StructB the variable myImage is of type UIImage?. I want the variable myImage to be able to have either URL? or UIImage? as the type so that I can have a single struct instead of two. Can someone help?
I tried to use Generics on the struct so that I can assign structs of different type to the same variable..but couldn't achieve that.
If you want a single instance to be able to contain either a URL or a UIImage, you need to use an enum
struct StructAorB: Codable {
enum ImageKind: Codable
{
case url(URL)
case image(UIImage) // NB UIImage is not Codable, but this is irrelevant to the question
// You'll just need to extend it to conform
case none
}
let title: String?
let subtitle: String?
let myImage: ImageKind
}
If you find it inconvenient to have switches everywhere, create convenience properties
extension StructAorB
{
var imageAsUrl: URL?
{
switch myImage
{
case .url(let ret):
return ret
default:
return nil
}
}
}

How to determine and set the type of Object of a parameter from a different parameter in JSON in Swift?

How can I implement a solution where I could set the type of a parameter on the go while parsing a JSON by analyzing the parameter at the same level as the other parameter?
let sampleData = Data("""
[
{
"type": "one",
"body": {
"one": 1
},
.
.
.
},
{
"type": "two",
"body": {
"two": 2,
"twoData": "two"
},
.
.
.
}
]
""".utf8)
struct MyObject: Codable {
let type: String
let body: Body
}
struct Body: Codable {
let one, two: Int?
let twoData: String?
}
print(try JSONDecoder().decode([MyObject].self, from: sampleData))
Here you can see that the keys in Body are all optionals. My solution requires they being parsed into different types according to the value given in the parameter type. How can I parse body into the following 2 separate types according to the value I receive in type?
struct OneBody: Decodable {
let one: Int
}
struct TwoBody: Decodable {
let two: Int
let twoData: String
}
The approach that I finally went with is with a custom init(from decoder: Decoder) method where the type is determined first and then using a switch statement iterate through each case to decode each type and assign it to the body as another enum. Here is the code:
struct MyObject: Decodable {
let body: MyBody
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(MyType.self, forKey: .type)
switch type {
case .one:
body = .one(try container.decode(OneBody.self, forKey: .body))
case .two:
body = .two(try container.decode(TwoBody.self, forKey: .body))
}
}
enum CodingKeys: String, CodingKey {
case type, body
}
}
enum MyType: String, Decodable {
case one, two
}
enum MyBody: Decodable {
case one(OneBody)
case two(TwoBody)
}
struct OneBody: Decodable {
let one: Int
}
struct TwoBody: Decodable {
let two: Int
let twoData: String
}
print(try JSONDecoder().decode([MyObject].self, from: sampleData))
PS: Please post an answer or a comment if there are any ideas for a better approach.

Conforming an array to Identifiable in SwiftUI

I have a little question about conforming to Identifiable in SwiftUI.
There are situations where we are required to have a given type MyType to conform to Identifiable.
But I am facing a case where I am required to have [MyType] (an array of MyType) to conform to Identifiable.
I have MyType already conforming to Identifiable. What should I do to also make [MyType] to conform to Identifiable?
I suggest embedding [MyType] in a struct, then having the struct conform to Identifiable. Something like this:
struct MyType: Identifiable {
let id = UUID()
}
struct Container: Identifiable {
let id = UUID()
var myTypes = [MyType]()
}
Usage:
struct ContentView: View {
let containers = [
Container(myTypes: [
MyType(),
MyType()
]),
Container(myTypes: [
MyType(),
MyType(),
MyType()
])
]
var body: some View {
/// no need for `id: \.self`
ForEach(containers) { container in
...
}
}
}
You can write an extension to conform an Array to Identifiable.
Since extensions can't contain stored properties, and also because it makes sense that two arrays that are the "same" to also have the same id, you'd need to compute the id based on the contents of an array.
The simplest approach here is if you can conform your type to Hashable:
extension MyType: Hashable {}
This also makes [MyType] conform to Hashable, and since id could be any Hashable, you could use the array itself as its own id:
extension Array: Identifiable where Element: Hashable {
public var id: Self { self }
}
Or, if you want, the id could be an Int:
extension Array: Identifiable where Element: Hashable {
public var id: Int { self.hashValue }
}
Of course, you can do this just for your own type where Element == MyType, but that type would need to be public.

SwiftUI allow all string enums in Struct

It's probably a stupid question but I'm trying to allow all string enums as type for a variable in a Struct. The code below is completely wrong but I hope that it will make my question clearer.
My idea was to conform all enums to the same protocol but I can't access .allCases with this approach.
My goal is that I can pass any string enum to the ListView which will then display all components of the enum (here: one; two; three).
Does anybody have an idea how to do this? It must be a very basic Swift thing but I wasn't able to figure it out searching through the web. Thank you so much!
import SwiftUI
struct ContentView: View {
var body: some View {
ListView(myEnum: Elements.self) //error here as well
}
}
protocol StringRepresentable {
var rawValue: String { get }
}
enum Elements: String, Equatable, CaseIterable, StringRepresentable {
case one
case two
case three
}
struct ListView: View {
let myEnum: StringRepresentable //doesn't work
var body: some View {
ForEach(myEnum.allCases, id: \.self) { elem in //can't access .allCases
Text(elem.rawValue)
}
}
}
There's several errors in your original code. First you weren't using a VStack (or List or LazyVStack) so your foreach would only render one element.
Secondly, you were passing the type, not the elements itself to the list view. And finally, your own StringRepresentable protocol is unnecessary, you can use RawRepresentable with it's associated type RawValue constrained to String
i.e. something like this:
struct ContentView: View {
var body: some View {
VStack {
ListView(values: Fruits.allCases)
ListView(values: Animals.allCases)
}
}
}
enum Fruits: String, CaseIterable {
case apple
case orange
case banana
}
enum Animals: String, CaseIterable {
case cat
case dog
case elephant
}
struct ListView<T: RawRepresentable & Hashable>: View where T.RawValue == String {
let values: [T]
var body: some View {
LazyVStack {
ForEach(values, id: \.self) { elem in
Text(elem.rawValue)
}
}
}
}
Which renders like this
Here is a variant that will work by sending in all items of an enum to the view rather that the enum itself and making the view generic.
struct ContentView: View {
var body: some View {
ListView(myEnum: Elements.allCases)
}
}
protocol StringRepresentable {
var rawValue: String { get }
}
enum Elements: String, Equatable, CaseIterable, StringRepresentable {
case one
case two
case three
}
struct ListView<T: CaseIterable & StringRepresentable>: View {
let myEnum: [T]
#State private var selectedValue: String = ""
var body: some View {
ForEach(0..<myEnum.count) { index in
Text(myEnum[index].rawValue)
.onTapGesture {
selectedValue = myEnum[index].rawValue
}
}
}
}

Does not conform to protocol Decodable

I have the following code which represents a Hockey Stick and some information about it. I have an issue where the stick isn't conforming to Decodable. I understand that every type used in the struct needs to also be codeable, and they are. For some reason however the "var conditions" line causes the error that I am unsure how to fix. Thank you!
enum StickLocation: Int, Codable, Hashable, CaseIterable {
case handle, mid, bottom
}
enum StickCondition: Int, Codable, Hashable, CaseIterable {
case pristine, scuffed, damaged, broken
}
struct HockeyStick: Identifiable, Codable {
var barcode: Int
var brand: String
var conditions: [StickLocation:(condition:StickCondition, note:String?)] // Offending line
var checkouts: [CheckoutInfo]
var dateAdded: Date
var dateRemoved: Date?
// Conform to Identifiable.
var id: Int {
return self.barcode
}
// If the stick was never removed then its in service.
var inService: Bool {
return self.dateRemoved == nil
}
}
The value type of your conditions dictionary is (StickCondition, String?), which is a tuple. Tuples are not Decodable/Encodable, and you cannot make them conform to protocols, so to fix this I recommend you make a new struct to replace the tuple like this:
enum StickLocation: Int, Codable, Hashable, CaseIterable {
case handle, mid, bottom
}
enum StickCondition: Int, Codable, Hashable, CaseIterable {
case pristine, scuffed, damaged, broken
}
struct StickConditionWithNote: Codable, Hashable {
var condition: StickCondition
var note: String?
}
struct HockeyStick: Identifiable, Codable {
var barcode: Int
var brand: String
var conditions: [StickLocation: StickConditionWithNote]
var checkouts: [CheckoutInfo]
var dateAdded: Date
var dateRemoved: Date?
// Conform to Identifiable.
var id: Int {
return self.barcode
}
// If the stick was never removed then its in service.
var inService: Bool {
return self.dateRemoved == nil
}
}

Resources