Organizing Your Networking Code in Swift 3 (with NASA data!)

Erica Millado
Yay It’s Erica
Published in
6 min readJul 8, 2017

In this blog post, I’ll review an example of how I organized my networking code to reach the NASA Mars Rover API. In my example app, I access the NASA API to retrieve a couple of images from one of the various Rover cameras. You’ll see how I implemented my API call with enums and separate files to keep my code organized.

The UI:

I created the UI in Storyboard and utilized a simple collection view.

The Networking:

Step 1: Decide on which NASA API I want to use and retrieve an apiKey. I decided to use the Mars Rover photos, for which you can query different cameras (ex. FHAZ “Front Hazard Avoidance Camera”, RHAZ “Rear Hazard Avoidance Camera”, etc.)

Step 2: In a file called NasaAPI.swift, create an enum for each type of camera that I may want to retrieve data for.

Step 3: In a file Photo.swift create our data model: photo.

Step 4: Back in our NasaAPI.swift file, create a struct named NasaAPI that will be responsible for creating the URL with its specific query items.

Step 5: Create a private static property of the NASA base url string.

Step 6: Create a private static property of your personalized apiKey (created on the NASA site).

Step 7–13: Create a function that will combine query elements and your base URL string to output a URL.

7 — this function should take in the camera type (the enum we created in step 2) and return a URL

8 — we create components, a URLcomponents property that starts with our baseURLString

9 — we create queryItems, an array of query items that will be included in our URL

10 — we create baseParameters, dictionary of type [String: String] that will include our query specifics for our URL

11 — we loop over our array of baseParameters array and create each dictionary item as a URLQuery item and append it to our queryItems array.

12 — we assign our queryItems array to our components.

13 — we return the url created from our components. Note that I have a print statement on line #53 so I check to see what the finished url looks like.

Step 14: Above the nasaURL function, I create a property that will hold on to the returned URL. Note that I’ve chosen to retrieve photos from the RHAZ (Rear Hazard Avoidance Camera)

Step 15–17: The JSON data will look like the image below. We write a function that will create Photo objects from each photo dictionary.

Note the dictionary keys we want in blue rectangles.

15 — Write a function that will take in a JSON dictionary and return an optional Photo.

16 — from the parsed JSON dictionary, create our photo properties: photoID (which is actually an Int that we cast into a string — see line #95), URL, and earth_date.

17 — from each of these unwrapped properties, we create a Photo and return it.

Step 18–20: We need to have a way of recognizing if our API call was successful or not. We also need to have a function that creates the networking call and processes our NASA photos request. Let’s create a file called PhotoStore.swift that will contain an enum for our PhotosResult (success or failure) and a PhotoStore class that will handle our networking calls.

18 — We create a PhotosResult enum with two cases that have associated values of either an array of Photos (for success) or an error (for failure).

19 — We create a custom error enum called NasaError to handle when we receive data from NASA but are not able to parse the JSON as we expected to.

20 — We create our PhotoStore class.

Step 21–28: Head back to our NasaAPI.swift file so we can create a function that will handle taking our returned JSON (from a networking call that will happen in the PhotoStore.swift file we just created) and breaking it into dictionaries that will become Photo objects.

21 — Our photos(fromJSON data:) function will return a PhotosResult (that enum we created in step 18).

22 — Because creating JSON objects throws an error, we do/try/catch here. We create a jsonObject from our data parameter.

23 — We cast our jsonObject as a jsonDictionary [AnyHashable: Any] and then parse the jsonDictionary for the “photos” key, which returns photosArray, an array of dictionaries. If this fails, we return a case of failure (a PhotosResult).

24 — We create an array of finalPhotos which will contain our Photo objects.

25 — We loop through our photosArray (from our jsonDictionary) and create photos from the photo(fromJSON json:) function we wrote earlier in this NasaAPI.swift file. We take these photo objects and append them to our finalPhotos array.

26 — We check that if we weren’t able to append Photo objects to our finalPhotos array BUT we were able to get JSON data back, we return an error that specifies we had invalid JSON data.

27 — We return a successful PhotosResult case with our finalPhotos array!

28 — In our catch block, we return a failure error case of PhotosResult.

Step 29: Head back to PhotoStore.swift to start setting up our Networking logic. Create a private session property of type URLSession.

Step 30–32: Create a processPhotosRequest(data:error:) function that will take in data, unwrap it, create photo objects if possible and return a PhotosResult.

31 — unwrap the data and if impossible, return a failure PhotosResult.

32 — Call on the NasaAPI.photo(fromJSON:jsonData) method that will return either a successful PhotosResult with a Photos array or failure.

Step 33–39: Implement a method that creates a URLRequest on our URLSession and has a completion handler with a PhotosResult.

34 — Call on NasaAPI.roverURL to retrieve our created URL.

35 — Create a request with this url.

36 — Create a dataTask with our request that we call on our URLSession.

Line #49 should say 37, not 47.

37 — Create a result property that we create from the output from our earlier processPhotosRequest(data:error:) function (see in orange rectangles).

38 — We set our completion with our PhotosResult. Note how I add this completion to the main queue since I know that our View Controller will be displaying the returned Photo images as UI elements.

39 — We resume our data task.

Step 40: We allow arbitrary loads in our Info.plist file. This change allows us to circumvent this error:

Transport security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app’s Info.plist file.

Build and run!

Check out the result from the RHAZ camera:

Check out the result from the Mast camera:

The repo for this project can be found here.

--

--