TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Day 52 Project 10 Challenge 3: Having Trouble Solving It

Forums > 100 Days of SwiftUI

Hi, I've tried to solve Project 10 Challenge 3 and I saw a couple of posts here, but none of them seemed to work.

Questions:

  1. Why does the struct Order still not conform to Codable?
  2. I also get the message in AddressView that "Generic struct 'ObservedObject' requires that 'Order' conform to 'ObservableObject'". How is that possible? Since ObservableObject applies to a class.
  3. I also get the error message from ContentView: "Generic parameter 'Destination' cannot be inferred" on NavigationLink when I try Challenge 3.
  4. Also this error message in CheckoutView "Value of type 'OrderClass' has no dynamic member 'order' using key path from root type 'Order'". That seems strange since OrderClass does contain a property that is an order struct, which then has a property called cost.

Here is my code:

Order

iimport SwiftUI

class OrderClass : ObservableObject, Codable {
    @Published var order = Order()

    enum CodingKeys : CodingKey {
        case order
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        order = try container.decode(Order.self, forKey: .order)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(order, forKey: .order)
    }
}

struct Order : Codable {

    static let types = ["Vanilla", "Strawberry", "Chocolate", "Rainbow"]

    var type = 0
    var quantity = 3

    var specialRequestEnabled = false {
        didSet {
            if specialRequestEnabled == false {
                extraFrosting = false
                addSprinkles = false
            }
        }
    }

    var extraFrosting = false
    var addSprinkles = false

    var name = ""
    var streetAddress = ""
    var city = ""
    var zip = ""

    var hasValidAddress : Bool {
        if name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || streetAddress.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || city.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || zip.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            return false
        }
        return true
    }

    var cost : Double {
        var cost = Double(quantity) * 2

        // complicated cakes cost more
        cost += (Double(type) / 2)

        if extraFrosting {
            cost += Double(quantity)
        }

        if addSprinkles {
            cost += Double(quantity) / 2
        }

        return cost
    }
}

CheckOut View

import SwiftUI

struct CheckoutView: View {
    @ObservedObject var order : Order

    @State private var confirmationMessage = ""
    @State private var showingConfirmation = false

    @State private var networkFailureMessage = ""
    @State private var networkingFailure = false

    func placeOrder() async {

        guard let encoded = try? JSONEncoder().encode(order) else {
            print("Failed to encode order")
            return
        }

        let url = URL(string: "https://reqres.in/api/cupcakes")!
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST"

        do {
            let (data, _) = try await URLSession.shared.upload(for: request, from: encoded)
            let decodedOrder = try JSONDecoder().decode(Order.self, from: data)
            confirmationMessage = "Your order for \(decodedOrder.quantity)x \(Order.types[decodedOrder.type].lowercased()) cupcakes is on its way !"
            showingConfirmation = true
        }
        catch {
            networkingFailure = true
            networkFailureMessage = "Lost internet connection."
        }

    }

    var body: some View {
        ScrollView {
            VStack {
                AsyncImage(url: URL(string: "https://hws.dev/img/cupcakes@3x.jpg"), scale: 3) { image in
                    image
                        .resizable()
                        .scaledToFit()
                } placeholder: {
                    ProgressView()
                }
                .frame(height: 233)

                Text("Your total is \(order.order.cost, format: .currency(code: "USD"))")
                    .font(.title)

                Button("Place Order") {
                    Task {
                        await placeOrder()
                    }
                }
                .padding()
            }
        }
        .navigationTitle("Check Out")
        .alert("Thank you", isPresented: $showingConfirmation) {
            Button("OK") {  }
        } message: {
            Text(confirmationMessage)
        }
        .alert("Networking Failure", isPresented: $networkingFailure) {
            Button("Continue") {  }
        } message: {
            Text(networkFailureMessage)
        }
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct CheckoutView_Previews: PreviewProvider {
    static var previews: some View {
        CheckoutView(order: Order)
    }
}

AddressView:

import SwiftUI

struct AddressView: View {
    @ObservedObject var order : Order

    var body: some View {
        Form {
            Section {
                TextField("Name", text: $order.name)
                TextField("Street Address", text: $order.streetAddress)
                TextField("City", text: $order.city)
                TextField("Zip Code", text: $order.zip)
            }

            Section {
                NavigationLink {
                    CheckoutView(order: order)
                } label: {
                    Text("Check Out")
                }
            }
            // if this condition satisfies, there won't be a new view leading to Check Out.
            .disabled(order.hasValidAddress == false)
        }
        .navigationTitle("Delivery Details")
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct AddressView_Previews: PreviewProvider {
    static var previews: some View {
        AddressView(order: Order())
    }
}

ContentView:

import SwiftUI

struct ContentView: View {
    // When @Published properties change, StateObject updates them.
    @State var order = Order()

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Select your cake type", selection: $order.type) {
                        ForEach(Order.types.indices) {
                            Text(Order.types[$0])
                        }
                    }

                    Stepper("Number of cakes: \(order.quantity)", value: $order.quantity, in: 3...20)
                }

                Section {
                    Toggle("Any special requests?", isOn: $order.specialRequestEnabled.animation())

                    if order.specialRequestEnabled {
                        Toggle("Add extra frosting", isOn: $order.extraFrosting)
                        Toggle("Add extra sprinkles", isOn: $order.addSprinkles)
                    }
                }

                Section {
                    NavigationLink {
                        AddressView(order: order)
                    } label: {
                        Text("Delivery Details")
                    }
                }

            }
            .navigationTitle("CupCake Corner")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

3      

HINT:

In your CheckOutView struct, how did you declare the variable order ??

All the other errors you list are probably side effects of one error. Once you solve the big problem, the others may very well disappear.

4      

@Obelix:

Thank you for the hint. I declared order in Checkout View as @State var order : Order since it is impossible to make order Struct conform to the ObservableObject protocol. A similar change was also made in AddressView.

I do observe that in Challenge 3, with a Struct approach instead of a Class, when I enter the address details and then lick "back", and then go to the address page again, all information is lost, which I believe speaks to the "value type vs. reference type" thing ?

3      

Right on!

I love it when the lightbulbs come on. Nice.

I may have ranted a few times about naming objects. If my coding team were to peer-review your code, this would certainly be one of the team's comments.

I think you tripped yourself up by taking shortcuts with your struct and class names. While you're learning, feel free to be overly verbose when naming your structs and classes. For one, it helps you solidify the concepts in your head. Verbose names also help you with your logic.

Compare:

let theOneAndOnlyFaceUpCard = Card?  // Verbose name from Matching card game from CS193p course.
// compare these two game card names
let tappedCard = Card? // Is this the first? or the second tapped card? It's unclear.

Another good reason may have helped in your situation.

You have this:

struct CheckoutView: View {
    @ObservedObject var order : Order
    // snip.......

You used Order to describe a struct, then reused this word as the name of a variable. Yikes! Which are you using? The struct, the class, or the variable. Confusing.

Maybe a better name could be:

struct CheckoutView: View {
    @ObservedObject var customersCupcakeOrder : Order  // <-- Be verbose!
    // snip.......

3      

@Obelix:

I will certainly keep this in mind ! Thank again.

2      

Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.