Swift JSON Parser – Part 4

In the previous posts we coded a fully functional JSON parser completed with error handling and independent from the source of the data (the reader).

In the parser we used also a custom operator >>> to invoke a function on an optional (if this is not nil) and return the result.

Now we are going to refine it and, in the way, see other custom operators.

Inspiration for this post came to me from this wonderful post of Tony DiPasquale. My post is really like an explanation and simplification (to me, first) of that one.

Curry the Photo creation

We begin adding a new factory function to the struct Photo. It  simply creates a Photo object and it is curried:

With this new function we could (almost) use a code like this to create a Photo:

The problem with this code is that the parameter we are passing to create and all subsequent functions are optionals. But that functions do not get optionals, they get non-nil values.

We try to solve this defining a new operator:

Definition of f <^> o (f is a func, o is an optional):

if o is not null apply f to o and return the result. Return nil (.None) if o is nil or the result of f is nil. Here is the code:

The precedence is the default (100) so it is calculated after the <<< operator (150); this is what we want.

This is also called the fmap operator: “apply this function to this wrapped (optional) value”.

Now we can modify the first line of Photo creation code to:

The <^> operator applies Photo.create to the (optional) result of _jsonItem[“titolo”] >>> StringFromJSON.

For the subsequent lines we have to make a little modification. Remember that our operator <^> return an optional, so a is an optional and we cannot simply apply it. We need another operator very similar to <^> but that, instead of a func, works with an optional func:

Definition of f <*> o (f is an optional func, o is an optional):

if o is not null and f is not null apply f to and return the result. Return nil (.None) if o is nil or f is nil or the result of f is nil. Here the code:

This is also called the apply operator: apply this wrapped (optional) function to this wrapped (optional) value.

Now the third line can be written as:

And we can continue with all the Photo creation code:

In summary, we need two different operators because the first function (Photo.create) is a func but the others are really wrapped (optionals) funcs.

Let’s recap the code of handleData at this point:

 Refactor: move JSON parse inside Photo

The next step is very simple, move the json decoding inside the Photo struct. First define new Photo class method:

To make this compile we must move the StringFromJSON (and sisters) function in a global scope (for example just at the top of Parser file).

Now the parser code becomes simply:

Last step: the protocol and a compiler crash 🙁

Our last step is to make finally Parser completely independent from Photo. It will work with any object that is conform to the protocol:

And now substitute in Parser all the invocations to Photo with JSONDecodable.

Now our parser is really independent from Photo and can convert any type of data (whose type conforms to JSONDecodable).

BUT

This last step causes the compiler to crash (really!). This is the bug I wrote about in my previous post: “a compiler crash“.

I hope you enjoy this multi post parser and, while waiting for Apple to fix its bug, good swift to you.

As usual the code of this project is at Github.

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

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

2 thoughts on “Swift JSON Parser – Part 4”

  1. Ciao Valerio,

    fantastico tutorial.
    Volevo chiederti, come posso fare per leggere tutto il file con il tuo parser e poi estrarre un a sola riga?

    Grazie mille in anticipo!

Leave a Reply

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