NEW: Join my free 100 Days of SwiftUI challenge today! >>

Sending and receiving orders over the internet

Paul Hudson    @twostraws   

iOS comes with some fantastic functionality for handling networking, and in particular the URLSession class makes it surprisingly easy to send and receive data. If we combine that with Codable to convert Swift objects to and from JSON, and URLRequest, which lets us configure exactly how data should be sent, we can accomplish great things in about 20 lines of code.

First, let’s create a method we can call from our Place Order button – add this to CheckoutView:

func placeOrder() {
}

Now modify the Place Order button to this:

Button("Place Order") {
    self.placeOrder()
}

Inside placeOrder() we need to do three things:

  1. Convert our current order object into some JSON data that can be sent.
  2. Prepare a URLRequest to send our encoded data as JSON.
  3. Run that request and process the response.

The first of those is straightforward, so let’s get it out of the way. We’ve made the Order class conform to Codable, which means we can use JSONEncoder to archive it by adding this code to placeOrder():

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

The second step – preparing a URLRequest to send our data – requires some more thought. You see, we need to attach the data in a very specific way so that the server can process it correctly, which means we need to provide two extra pieces of data beyond just our order:

  1. The HTTP method of a request determines how data should be sent. There are several HTTP methods, but in practice only GET (“I want to read data”) and POST (“I want to write data”) are used much. We want to write data here, so we’ll be using POST.
  2. The content type of a request determines what kind of data is being sent, which affects the way the server treats our data. This is specified in what’s called a MIME type, which was originally made for sending attachments in emails, and it has several thousand highly specific options.

So, the next code for placeOrder() will be to create a URLRequest, configure it to send JSON data using a HTTP POST, and attach our data.

Of course, the real question is where to send our request, and I don’t think you really want to set up your own web server in order to follow this tutorial. So, instead we’re going to use a really helpful website called https://reqres.in – it lets us send any data we want, and will automatically send it back. This is a great way of prototyping network code, because you’ll get real data back from whatever you send.

Add this code to placeOrder() now:

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

Notice how I added a force unwrap for the URL(string:) initializer. Creating URLs from strings might fail because you inserted some gibberish, but here I hand-typed the URL so I can see it’s always going to be correct – there are no string interpolations in there that might cause problems.

At this point we’re all set to make our network request, which we’ll do using URLSession.shared.dataTask() and the URL request we just made. Remember, if you don’t call resume() on your data task it won’t ever start, which is why I nearly always write the task and call resume before actually filling in the body.

So, go ahead and add this to placeOrder():

URLSession.shared.dataTask(with: request) { data, response, error in
    // handle the result here.
}.resume()

Now for the important work: we need to read the result our request. If something went wrong – perhaps because there was no internet connection – we’ll just print a message and return.

Add this to placeOrder():

guard let data = data else {
    print("No data in response: \(error?.localizedDescription ?? "Unknown error").")
    return
}

If we make it past that, it means we got some sort of data back from the server. Because we’re using the ReqRes.in, we’ll actually get back to the same order we sent, which means we can use JSONDecoder to convert that back from JSON to an object.

To confirm everything worked correctly we’re going to show an alert containing some details of our order, but we’re going to use the decoded order we got back from ReqRes.in. Yes, this ought to be identical to the one we sent, so if it isn’t it means we made a mistake in our coding.

Showing an alert requires properties to store the message and whether it’s visible or not, so please add these two new properties to CheckoutView now:

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

We also need to attach an alert() modifier to watch that Boolean, and show an alert as soon as its true. Add this modifier to the GeometryReader in CheckoutView:

.alert(isPresented: $showingConfirmation) {
    Alert(title: Text("Thank you!"), message: Text(confirmationMessage), dismissButton: .default(Text("OK")))
}

And now we can finish off our networking code: we’ll decode the data that came back, use it to set our confirmation message property, then set showingConfirmation to true so the alert appears. If the decoding fails – if the server sent back something that wasn’t an order for some reason – we’ll just print an error message.

Add this final code to placeOrder(), just inside the completion closure for dataTask(with:):

if let decodedOrder = try? JSONDecoder().decode(Order.self, from: data) {
    self.confirmationMessage = "Your order for \(decodedOrder.quantity)x \(Order.types[decodedOrder.type].lowercased()) cupcakes is on its way!"
    self.showingConfirmation = true
} else {
    print("Invalid response from server")
}

With that final code in place our networking code is complete, and in fact our app is complete too. If you try running it now you should be able to select the exact cakes you want, enter your delivery information, then press Place Order to see an alert appear!

We’re done! Well, I’m done – you still have some challenges to complete!

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5