UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Cupcake Corner server not reflecting right quantity of cupcakes

Forums > 100 Days of SwiftUI

Hi,

When placing an order for e.g. 8 cupcakes in ContentView, then navigating through placing the order, when i receive the notification back from the server, it does not update the decodedOrder quantity correctly - but it reflects the right kind of cupcake..??

I have checked contentView, and I have made the binding correctly to quantity.

struct ContentView: View {
    //With "State" the order is 'made' here...
    @State private var order = Order()

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    Picker("Select your cake type", selection: $order.type) {
                        ForEach(Order.types.indices, id: \.self) {
                            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 sprinkles", isOn: $order.addSprinkles)
                    }
                }

                Section {
                    Text("Total cost: \(order.price, format: .currency(code: order.localCurrency))")
                }

                Section {
                    NavigationLink("Delivery details") {
                        AddressView(order: order)
                    }
                }
            }
            .navigationTitle("Cupcake Corner - Day 52")
        }
    }
}

I also believe the data upload is also correct. Anyone have any ideas?

   struct CheckOutView: View {
    var order: Order

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

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

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

                Button("Place order") {
                    //make a task to send order to the web
                    Task {
                        await placeOrder()
                    }
                }
                .padding()
            }
        }
        .navigationTitle("check out")
        .navigationBarTitleDisplayMode(.inline)
        .scrollBounceBehavior(.basedOnSize) //Bounce scroll only when contents require it...
        .alert("Thank you!", isPresented: $showingConfirmation) {
            Button("OK") { }
        } message: {
            Text(confirmationMessage)
        }
        .alert("Error", isPresented: $showingRejection) {
            Button("OK") { }
        } message: {
            Text(rejectionMessage)
        }
    }

    //uploading data
    func placeOrder() async {
        //Encode order to JSON
        guard let encoded = try? JSONEncoder().encode(order) else {
            print("Failed to encode order")
            return
        }

        //create the url that we will send the data to
        let url = URL(string: "https://reqres.in/api/cupcakes")! //ideally you want to handle if the url does not exist
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpMethod = "POST" //We want to write/post data

        //actually upload data or catch error
        do {
            let (data, _) = try await URLSession.shared.upload(for: request, from: encoded)

            //Decode the order that we receive back from the server if it was sent
            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 {
            rejectionMessage = "There was an error placing your order. please try again."
            showingRejection = true
            print("Check out failed: \(error.localizedDescription)")
        }
    }
}

Thanks.

2      

How do you verify the object in one view is properly packaged and received by another view?

Debugging Technique

Here's a common technique you may want to consider.

You may have structs or classes that contain strings, integers or booleans. Or all three! While you're developing and testing, you may want to just see the contents of an object.

One way to consider is making your object conform to CustomStringConvertible protocol. This protocol requires you to craft a computed var with the name description. You have full freedom how you want to craft this string variable. Maybe you only want three or four of your object's properties. Then benefit is you're able to toss in a print statements here and there to see the contents of your objects before they are sent, after they are updated, and how they are received by other views.

Here's a small code snip. Run this in Playgrounds and have fun.

class Order {
    // Small subset of an actual order object...
    var cakeType:      String
    var quantity:      Int
    var withSprinkles: Bool

    init(cakeType: String, quantity: Int, withSprinkles: Bool = false) {
        self.cakeType      = cakeType
        self.quantity      = quantity
        self.withSprinkles = withSprinkles
    }
}

extension Order: CustomStringConvertible {
    // Custom string convertible requires
    // a var named description.
    // YOU have full freedom to build a custom
    // string out to describe your Order Object

    // Boolean converter
    var sprinkleString : String { withSprinkles ? "Yes" : "None" }
    var description:     String { """
    ========== ORDER ==========
    Flavor:    \(cakeType)
    Quantity:  \(quantity)
    Sprinkles: \(sprinkleString)
    ===========================
    """
    }
}

// Create an object
let partyCupcakes = Order(cakeType: "Vanilla", quantity: 12)

// Toss this in your views to help with validation
print( partyCupcakes.description)

// ========== ORDER ==========
// Flavor:    Vanilla
// Quantity:  12
// Sprinkles: None
// ===========================

Keep Coding!

Please return here and let us know if this helped you zero in on the location of your bug?

3      

Hi there. Solved it. Forgot to add quantity to the codingKeys enum, which meant that it was not handled by the server.

@Observable
class Order: Codable {
    //Adding coding keys to ensure that when data is sent via the server, the data we get back is clean without "_" and other things that make it difficult to use decoded data
    enum CodingKeys: String, CodingKey {
        case _type = "type"
        case _quantity = "quantity" //Forgot to add this parameter
        case _specialRequestEnabled = "specialRequestEnabled"
        case _extraFrosting = "extraFrosting"
        case _name = "name"
        case _street = "street"
        case _city = "city"
        case _zip = "zip"
        case _localCurrency = "localCurrency"
    }

    static let types = ["Vanila", "Strawberry", "Chokolate", "Rainbow"]

    var type = 0
    var quantity = 3

    ...more class code below

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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

Reply to this topic…

You need to create an account or log in to reply.

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.