www.erlang-solutions.com
SWIFTBA
MEETUP
JUNE 8th, 2016
@ Inaka's Offices
www.erlang-solutions.com
HELLO!
Pablo Luciano Villar
pablo@inakanetworks.com
@pablolvillar
www.erlang-solutions.com www.inaka.net
_iOS dev since 2011
_swifter since 2015
www.erlang-solutions.com
#warilis
www.erlang-solutions.com
writing
a
rest
interconnection
library
in
swift_
www.erlang-solutions.com
ROADMAP
1. Motivation
2. Architectural concept
3. Implementation
www.erlang-solutions.com
1.
MOTIVATION
www.erlang-solutions.com
Why would we need a REST
interconnection library?
Our App
Server
Backend
Give me a list with all the users
200: Success, returns
JSON array with users
www.erlang-solutions.com
Why would we need a REST
interconnection library?
Create a user with this info: [...]
200: Success,
user created
Our App
Server
Backend
www.erlang-solutions.com
Why would we need a REST
interconnection library?
Update some info for this user:
[...]
200: Success, returns
user updated
Our App
Server
Backend
www.erlang-solutions.com
Delete this user: [...]
200: Success,
user deleted
Why would we need a REST
interconnection library?
Our App
Server
Backend
www.erlang-solutions.com
Why would we need a REST
interconnection library?
C/R/U/D
Response
Our App
Server
Backend
www.erlang-solutions.com
● NSURLSession
● Alamofire / AFNetworking
● Other libraries
How we usually write networking code:
www.erlang-solutions.com
NSURLSession example: "Give me a list with all the users"
GET /users
How we usually write networking code:
www.erlang-solutions.com
Now, where should that networking
code go…?
www.erlang-solutions.com
Premises:
● Find a good place to put our networking
code.
● Define a proper way to architecture our
REST layer.
● Avoid duplicated code from common
CRUD operations.
● Come up with a concise API that we can
use across all of our projects.
www.erlang-solutions.com
2.ARCHITECTURAL
CONCEPT
www.erlang-solutions.com
GOAL:
Provide a neat API
that our
ViewControllers can
interact with
without having to worry
about networking
implementation details.
www.erlang-solutions.com
Let's find out common paths...
"Give me a list with all the users" GET /users
"Give me the details for this user" GET /users/:id
"Create a user with this data" POST /users
"Update this user with this data" PUT /users/:id
"Delete this user" DELETE /users/:id
www.erlang-solutions.com
Let's find out common paths...
"Give me a list with all the users" GET /users
"Give me the details for this user" GET /users/:id
"Create a user with this data" POST /users
"Update this user with this data" PUT /users/:id
"Delete this user" DELETE /users/:id
www.erlang-solutions.com
Let's find out common paths...
"Give me a list with all the posts" GET /posts
"Give me the details for this post" GET /posts/:id
"Create a post with this data" POST /posts
"Update this post with this data" PUT /posts/:id
"Delete this post" DELETE /posts/:id
www.erlang-solutions.com
www.erlang-solutions.com
"Give me a list with all the entities" GET /:name
"Give me the details for this entity" GET /:name/:id
"Create an entity with this data" POST /:name
"Update this entity with this data" PUT /:name/:id
"Delete this entity" DELETE /:name/:id
Let's introduce the entity concept...
www.erlang-solutions.com
"Give me a list with all the entities" GET /:name
"Give me the details for this entity" GET /:name/:id
"Create an entity with this data" POST /:name
"Update this entity with this data" PUT /:name/:id
"Delete this entity" DELETE /:name/:id
Notice that any entity needs an id to work
www.erlang-solutions.com
User Post Comment
id Create?
Read?
Update?
Delete?
Entity
www.erlang-solutions.com
REPOSITORY!
Actually, we need a different place
from which we can perform CRUD
operations to our entity…
Entity
Create
Read
Update
Delete
And that place is called…
www.erlang-solutions.com
User
Repository
User
findAll() → [User]
findByID(id) → User
create(User)
update(User)
delete(User)
www.erlang-solutions.com
Elemental CRUD operations*
Repository
Entity
findAll() → [Entity]
findByID(id) → Entity
create(Entity)
update(Entity)
delete(Entity)
www.erlang-solutions.com
Post
Repository
Post
findAll() → [Post]
findByID(id) → Post
create(Post)
update(Post)
delete(Post)
Customization
findPostsFromUser(User) → [Post]
www.erlang-solutions.com
*
www.erlang-solutions.com
This one has the issue that every repository you
create will come with all these basic CRUD
methods by default, even if any particular
repository doesn't need all of them.
That breaks the YAGNI principle.
But still, it's a very convenient approach for us.
* There are many ways to approach the
Repository pattern.
www.erlang-solutions.com
name
Our Repository needs a name to work
Repository
Entity
findAll() → [Entity]
findByID(id) → Entity
create(Entity)
update(Entity)
delete(Entity)
GET /:name/
GET /:name/:id
POST /:name
PUT /:name/:id
DELETE /:name/:id
www.erlang-solutions.com
User
Repository
findAll() → [User]
findByID(id) → User
create(User)
update(User)
delete(Entity)
GET /users/
GET /users/:id
POST /users
PUT /users/:id
DELETE /users/:id
This name represents the group of entities that the repository works with
"users"
User
www.erlang-solutions.com
Last, but not least…
We still need to define a place where
our networking code will fall into.
Let's introduce the concept of…
BACKEND
www.erlang-solutions.com
Repository Backend
GET
/users Networking
code
www.erlang-solutions.com
Repository
NSURLSession
Backend
GET
/users NSURLSession
code
www.erlang-solutions.com
Repository
Alamofire
Backend
GET
/users Alamofire
code
www.erlang-solutions.com
Repository
Testing
Backend
GET
/users Sample code
(mocks)
www.erlang-solutions.com
Repository Backend
GET
/users Networking
code
Raw Data
Parsing??
www.erlang-solutions.com
Raw Data
Parsing
User
Repository
NSURLSession
Backend
GET
/users
UsersList
ViewController .findAll()
Users
www.erlang-solutions.com
3.IMPLEMENTATION
www.erlang-solutions.com
Let's translate all
these diagrams into
code.
Not just any code,
but…
Swiftcode!
www.erlang-solutions.com
Where to start…?
OK, let's do Repository
www.erlang-solutions.com
✔ Protocol composition
✔ Generics
- Prefer composition over inheritance whenever
possible.
- This way, you end up having a more flexible and
extendable architecture.
- By using generics, you lead your models to
become more adaptive and customizable.
- The final user is going to be happier.
www.erlang-solutions.com
Before proceeding, let's introduce
two new friends you should become
familiar with...
● Result
● Future
www.erlang-solutions.com
● Result
- It's an enum that we're going to use to represent
a result.
- It can be either:
- Success (and hold a relevant result value)
- Failure (and hold a relevant error)
www.erlang-solutions.com
● Result
e. g. [User]
e. g. NSError
www.erlang-solutions.com
● Future
- You should definitely check out this talk:
- https://realm.io/news/swift-summit-javier-soto-futures/
- Anyway, roughly speaking, a Future is a struct
that we're going to use to represent the
computation of an asynchronous task.
www.erlang-solutions.com
- Here you can see why it's convenient to use
Futures:
www.erlang-solutions.com
www.erlang-solutions.com
.map and .andThen are 2 special
functions in Futures that allow
us to chain asynchronous
operations very nicely.
www.erlang-solutions.com
● Future
- Let's create a function that returns a Future:
This Future is going to work with a
Result<[User], NSError>
www.erlang-solutions.com
- Let's use that Future:
let result:
(Result<[User], NSError>)
Type inference
let users: [User]
let error: NSError
www.erlang-solutions.com
✔ Static typing / Type inference
✔ Enums with associated values
- Compiler enforces you to use the types you're
expected to use.
- Write less, know more.
- We're getting all the juice from Swift enums by
using them with associated values.
- This allows us to define powerful structures to
represent discrete values containing relevant
information in a proper way.
www.erlang-solutions.com
Now, let's add elemental CRUD
functionality to our Repository...
www.erlang-solutions.com
We can add default implementations
for those in a protocol extension:
www.erlang-solutions.com
Let's analyze how we would
implement one of those…
www.erlang-solutions.com
Let's add a Backend at the Repository
level...
www.erlang-solutions.com
Let's define a Backend:
Associated type: String
Means that any case will
have a .rawValue of type
String.
"GET" "POST" "PUT" "DELETE"
come by default
www.erlang-solutions.com
Let's (finally) add a Backend at the
Repository level...
www.erlang-solutions.com
let future:
Future<NSData?, NSError>
Type inference ≠Parsing
www.erlang-solutions.com
=
www.erlang-solutions.com
www.erlang-solutions.com
Now, let's define a concrete Backend
that we can actually use...
www.erlang-solutions.com
Now, let's define a concrete Backend
that we can actually use...
www.erlang-solutions.com
URL
checking We're going to improve this.
Later. Promise.
Server
linking
Dependency
www.erlang-solutions.com
We need variables to hold these.
Also, it would be cool to be able to inject them.
www.erlang-solutions.com
Let's go back a bit to our
NSURLSessionBackend definition...
We need to hold state, so we need a class.
www.erlang-solutions.com
Let's define our configuration object
www.erlang-solutions.com
We can use constructor injection in
our NSURLSessionBackend...
With default values
Remember: Using dependency injection helps unit-testing.
www.erlang-solutions.com
✔ Dependency Injection
✔ Enums with raw values
- Initializers with default values in their
parameters encourage constructor injection.
- Once again, Swift eases unit-testing.
- Enums can be associated to a type so that their
cases hold a .rawValue of that type.
- You can define their raw values by your own, or let
the compiler do its magic and use the defaults.
www.erlang-solutions.com
Now the question is, why the hell
are we still using NSError??
www.erlang-solutions.com
Alright, let's customize!
www.erlang-solutions.com
Let's enhance our error handling...
All of these do not appear as errors in the
networking process...
We usually get a success response with a status code that WE should
interpret as an error, depending on the code (e.g. a 500).
www.erlang-solutions.com
Now, let's talk about parsing...
Do I have time to talk about this?
YES / NO
www.erlang-solutions.com
OK, let's talk about parsing...
User[String: AnyObject]
a.k.a. "Dictionary"
conversion
www.erlang-solutions.com
To the
server
OK, let's talk about parsing...
User[String: AnyObject]
create()
update()
Dictionary
Representable
www.erlang-solutions.com
OK, let's talk about parsing...
User[String: AnyObject]
read()
From the
server
Dictionary
Initializable
www.erlang-solutions.com
From dictionary / To dictionary
In case there's a parsing error (e.g. a missing field).
Remember: You should NEVER initialize an invalid object.
www.erlang-solutions.com
From dictionary, example:
www.erlang-solutions.com
To dictionary, example:
www.erlang-solutions.com
✔ ErrorType
✔ Throws
- Error handling has been enhanced in Swift.
- Now you can define your own errors, which
combined with the power of enums and pattern
matching allow you to work in a cleaner way.
- Throwable initializers encourage better exceptions
handling mechanisms, such as try/catch.
- As we saw, you can make your initializers throw any
discrete ErrorType that you define.
www.erlang-solutions.com
https://github.com/inaka/JaymeMeet Jayme
✓ Open Source
✓ Issues are welcome
✓ So are Pull Requests
Jayme is basically what we just built, with some other
enhancements that I haven't talked about because of time, for
example:
● Pagination support (PagedRepository)
● More generalization (Backend is more abstract)
www.erlang-solutions.com
Meet Jayme https://github.com/inaka/Jayme
www.erlang-solutions.com
Meet Jayme https://github.com/inaka/Jayme
www.erlang-solutions.com
THANK YOU!
Any questions?
pablo@inakanetworks.com
@pablolvillar

Writing a REST Interconnection Library in Swift