Swift JSON Parser – Part 1

This is the first part of a multi-step process in which we will build a JSON parser in Swift.

In this first post we will see, among others, the following swift features in action:

  • optionals
  • enum with associated values
  • curried functions
  • typealias

We will do this only as an exercise to examine some Swift features. In fact many JSON libraries already exists and we could use them in a real app (see for example json-swift).

We begin now with a complete parser with full functionality. Then we will optimize it.

The data

Usually the data to parse comes from the web (i.e. from a REST service). To test our parser we will instead simulate it and use a simple file included in our project.

Open XCode and create a new “Single View Application” project. Name the product “ParserJson” and set the language to “Swift”.

ParserJson

Then download the following file, unzip and add it to the project:

The records in this file represent a list of photos, each one having the following columns:

  • titolo (the title of the photo)
  • autore (the photographer)
  • latitudine (the geo-location of the photo)
  • longitudine
  • data (the date of the shot)
  • descr (a description)

The Mapping

The aim of our parser is to convert the text from the file (JSON formatted) to something we can easily use in our program. This conversion is called “Mapping” and “something” will be an array of swift struct: structs usually are good to just contain data, but in Swift they can also methods, we will see this later.

We name this struct: Photo. So begin creating a new swift file (Photo.swift) and insert into it the following code:

Design

We are writing a parser that reads data from a file but we want to design it in such a way that it will be very simple, in the future, to change the kind of source (the WEB, a database etc…).

We will not pass to the parser a simple string containing the JSON, but a function it has to invoke in order to obtain the data. This separate the logic of the parser from the “source” of the data.

Note that this function could return the data asynchronously, for example if it has to download it from the web. It is not happening when reading data from a file, but, again, our design will contemplate the more general case.

Our parser class will receive a function it has to call in order to receive the JSON string.

This is the implementation of the function readJsonFile:

The Optionals

Note the use of optionalsfileData can be nil (the operation can fail) so it must be declared NSData?. The same is true for error (if all works right actually it is nil) and it is declared as  NSError?.

To tell to the completion handler the success or failure of the operation it must receive two parameters: NSData? and NSError?, two optionals.

Enum with associated value

The two optionals passed to the completion are really mutual. If one is nil, the other is not nil, and viceversa. We would like to pass to it a single parameter telling all the story.

Welcome to the enum with associated values:

If the enum evals to Value we are interested in the NSData. If the enum evals to Error we are interested in the NSError.

Using this enum we can modify the readJsonFile in the following way:

Note that we unwrap the error and fileData before passing it to the enum because the associated values are non optionals in our definition of the enum.

We will see shortly how to switch over the ReaderResult enum.

Curried function

In our design the Parser will call a reader function to obtain the data to process. Now we have a problem. The readJsonFile wants to know the name of the file to read, but the parser doesn’t know it (and it must not know, it is a generic parser).

One possible solution would be to pass the file name to the parser that, in turn, passes it to the reader function. But this is not so nice. Try another solution: the curried function.

Let’s change slightly the prototype of readJsonFile leaving the body identical:

This syntax allows us to call the method with only one parameter instead of two. In that case we will have as return value another func with only one parameter in the prototype.

So we will call the parser using the following code:

Note that parserTestReader is a function of prototype:

(what Parser was really expecting). It is really a reader of test (test.json).

Enum switch

Our class Parser will have the following structure:

The method start receive the data and then switch over the result handling the error or the data. Note the syntax let .Error … to use different parts of the enum in the different case of the switch.

typealias

The function called by the parser to receive data is so defined:

it just take in a ReaderResult parameter and return void.

Let us define a shortcut to name it, in order to avoid repetition (and mistyping). We can do this with the typealias command:

nothing magic but now we can use ParserReader when we want to refer to this function prototype.

Handle data

Let us pass to the parser another callback to be called at every new photo parsed:

Thus the call to the parser will be:

The method start of the parser will receive this callback and pass it to the handleData method. The closure is the last parameter of the method so we can write it after the closed parenthesis. This is called a Trailing Closure in swift.

The new start implementation is:

The handleData method has the following implementaiton:

The method JSONObjectWithData returns an object of type AnyObject? and we analyze it.

All the function is an optional binding over all the optionals.

We expect the json object to be an array. If it indeed is, we loop on the array. For each element we check if it is a dictionary and then optional bind the elements of the dictionary.

Only if all the optional binds are all successfull we create a new Photo object and pass it to the parserNewPhoto callback.

Error handling

Our error handling function simply report the problem in the console:

I’m sure you will implement a better one in your app.

Note that we are not handling error inside handleData. We will do this in the next post.

Summary

In this post we met some interesting swift features. The Parser is not finished. In the next post we will refactor it in a more swift way.

Code

The project of this post can be found on Github.

Swift JSON Parser – Part 2

 

Valerio Ferrucci

Valerio Ferrucci (valfer) develops software on Apple Macintosh since the 1990s until today for MacOS, OSX and, since some years, iOS. He is also a Web (PHP/MySQL/JS/CSS) and Android Developer.

More Posts - Website

Follow Me:
TwitterFacebookLinkedIn

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Leave a Reply

Your email address will not be published. Required fields are marked *