In this project students will send HTTP requests in Postman and then in Xcode Playgrounds. In the finished playground, they will perform a fetch for a Person and then a series of fetches for each Film in which the person appears.
- Find which endpoints to hit for people and films
- Look at a sample response (JSON)
- Determine how to structure your model
All data in this project is retrieved from https://swapi.co/. Because your project depends entirely on communication to the server, you need to determine two things:
- How/where to talk to the server (outgoing)
- How to understand the response (incoming)
- Visit the above URL and look at the documentation. The base URL is prominently displayed on a label.
https://swapi.co/api/. All requests to the server will begin with that URL. - The sample request has two components added to it -
people/, the person endpoint we'll be hitting, and1/the "ID" for a particular person. In this case, Luke Skywalker. - You have your Person endpoint, but you still need the Film endpoint. At the top-right, hit "Documentation" and then look for "Films".
- It has the same base URL but this time the endpoint is named
films/. According to the documentation, the Film endpoint also takes an ID. - You now have both of your endpoints.
-
Install Postman if you haven't already and open up a new request.
-
Paste the full URL for a Person and hit Send. If successful, Postman will print out JSON person information. If you aren't getting correct info, make sure your URL is formatted correctly.
https://swapi.co/api/people/{CHARACTER_ID} -
The field "films" is an array of URL strings. Copy one of these to your clipboard and open a new tab in Postman.
-
Paste the film URL and hit Send. The response JSON is your Film model.
-
You now have both JSON responses and are ready to declare your models in Swift.
-
In a new Swift playground, create a Person struct. Person is a Swift representation of JSON data, so its properties need to be based on the JSON you received in Postman.
-
Give Person
name: Stringandfilms: [URL]properties. Add any other properties you would also like to decode. Make sure to spell them identically to the JSON. -
Create a Film struct and, using the JSON, give it properties for
title,opening_crawlandrelease_date. It's ok that your property names are written in snake_case. In the future we'll learn strategies to avoid this. -
Conform both of your types to
Decodable.
-
Below your two custom types, create a class named
SwapiService. This class will be responsible for fetching data from SWAPI and parsing it into your models. -
Since both fetches use the same base URL, add it to the top level of your SwapiService.
static private let baseURL = URL(string: "https://swapi.co/api/") -
Declare a static function "fetchPerson" that takes an id of type
Intand a completion block.
static func fetchPerson(id: Int, completion: @escaping (Person?) -> Void) {
// 1 - Prepare URL
// 2 - Contact server
// 3 - Handle errors
// 4 - Check for data
// 5 - Decode Person from JSON
}
-
Step one is to prepare your URL. Unwrap the base URL and in the
elseclause, callcompletion(nil). -
Declare
finalURLby callingbaseURL.appendingPathComponent()and pass in the rest of the Person endpoint path. -
To contact the server, call
URLSession.shared.dataTask(with:completion:). Pass in your URL and then hover over the completion block and hit "return" to expand it. -
Add
.resume()to the closing bracket of your completion block. -
Name the arguments
data,_anderror. -
The next step is to handle errors inside the data task. Use
if-letto unwrap the optional error. Print it out and callcompletion(nil)if there is one. Any time completion is called,returnfrom the function as well. -
Use
guard letto unwrap the data. As before, call completion in theelseclause.
Because decoding is a throwing function, the rest of the data task will take place inside of a do-catch block.
-
In the
doportion. Declare an instance ofJSONDecoderand name it "decoder". -
Call the decoder to decode a Person from the data. You have to
tryin case it throws an error.let person = try decoder.decode(Person.self, from: data) -
If person successfully decodes, complete with it.
-
In the
catchblock, print out the error and complete withnil. -
Test out your fetch function by calling it and printing out the resulting Person. If it fails, your prior error handling should print an error describing why.
SwapiService.fetchPerson(id: 10) { person in
if let person = person {
print(person)
}
}
- Declare a static function that takes in a URL and completion block of type
(Film?) -> Void.
static func fetchFilm(url: URL, completion: @escaping (Film?) -> Void) {
// 1 - Contact server
// 2 - Handle errors
// 3 - Check for data
// 4 - Decode Film from JSON
}
This function takes in a completed URL, so you wont need to do any additional preparation on it.
-
Call
URLSession.shared.dataTask(with:completion:). Pass in the url, expand it and give the same argument names as before. -
Call
.resume()at the end of the completion block. -
Use
if-letto unwrap the error andguard letto unwrap data. Don't forget to callcompletion(nil)in both cases andreturn. -
Time to decode. Open up your
do-catchand declare anotherJSONDecoder. -
Use the same syntax as before to decode a film. If successful, complete with it.
-
Remember to handle errors and call completion in the
catchblock.
-
Declare a function called
fetchFilm(url: URL). Inside of that function, call your SwapiService and fetch a film from the passed in url. -
Print out the result of the fetch.
func fetchFilm(url: URL) {
SwapiService.fetchFilm(url: url) { film in
if let film = film {
print(film)
}
}
}
-
Below the print statement in your Person fetch. Create a
forloop and call your new function for each URL in the person's films array. -
If everything has been done correctly to this point, you are now able to pass in any person id and see the character info + all films in which it appears.