Slide 1

Slide 1 text

Encoding and Decoding in Swift Kaitlin Mahar Software Engineer @ MongoDB @k__mahar @kmahar

Slide 2

Slide 2 text

What is encoding? Encoder Swift type External representation etc... Scalar (e.g. Int) struct class enum

Slide 3

Slide 3 text

What is decoding? Decoder Swift type External representation etc... Scalar (e.g. Int) struct class enum

Slide 4

Slide 4 text

Why would I want to encode and decode data? ● Allows data transfer in and out of your application ○ Communicating with a REST API via JSON ○ Reading from and writing to a database ○ Importing and exporting data from files

Slide 5

Slide 5 text

Swift 4 introduced a standardized approach to encoding and decoding. How does it actually work?

Slide 6

Slide 6 text

Basic Usage

Slide 7

Slide 7 text

An Encodable type knows how to write itself to an Encoder.

Slide 8

Slide 8 text

Types With Built-In Encodable Support ● Numeric types ● Bool ● String ● If the values they contain are Encodable: ○ Array ○ Set ○ Dictionary ○ Optional ● Common Foundation types: URL, Data, Date, etc.

Slide 9

Slide 9 text

● Automatic conformance if all properties are Encodable ● Types can provide custom implementations ● Format agnostic: write it once, works with any Encoder!

Slide 10

Slide 10 text

A Decodable type knows how to initialize by reading from a Decoder.

Slide 11

Slide 11 text

Types With Built-In Decodable Support ● Numeric types ● Bool ● String ● If the values they contain are Decodable: ○ Array ○ Set ○ Dictionary ○ Optional ● Common Foundation types: URL, Data, Date, etc.

Slide 12

Slide 12 text

● Automatic conformance if all properties are Decodable ● Types can provide custom implementations ● Write it once, works with any Decoder

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

… and that's it! Making Types Codable

Slide 15

Slide 15 text

Using Encoders and Decoders

Slide 16

Slide 16 text

Using An Encoder JSONEncoder Encodable value of type T UTF-8 encoded Data

Slide 17

Slide 17 text

Using An Encoder { "name":"Roscoe", "color":"orange" }

Slide 18

Slide 18 text

Using A Decoder JSONDecoder Decodable value of type T Type to decode to, and UTF-8 encoded Data

Slide 19

Slide 19 text

Using A Decoder Data we got from encoding

Slide 20

Slide 20 text

Advanced Usage: Customizing How Your Types are Encoded/Decoded

Slide 21

Slide 21 text

{ "firstName":"Roscoe", "color":"orange" } Q: What if I want to rename a key? { "name":"Roscoe", "color":"orange" }

Slide 22

Slide 22 text

A: Use CodingKeys ● Nested type that specifies the keys that will be used for encoding ● Compiler generated, but custom implementation can be provided Compiler- generated default

Slide 23

Slide 23 text

Renaming a key { "firstName":"Roscoe", "color":"orange" }

Slide 24

Slide 24 text

Q: What if I want to modify properties as I encode them? { "name":"roscoe", "color":"orange" } { "name":"Roscoe", "color":"orange" } e.g. Convert a string to lowercase?

Slide 25

Slide 25 text

A: the Encodable.encode method

Slide 26

Slide 26 text

Encoder Single Value Unkeyed Keyed one value sequence of values key/value pairs provides containers: views into storage

Slide 27

Slide 27 text

Encoding containers support storing three types of values. nil Bool, String, Double, Float all Int and UInt types Encodable type base case 1: nil base case 2: primitives recursive case

Slide 28

Slide 28 text

So how are these containers used?

Slide 29

Slide 29 text

Encoder KeyedEncodingContainer name color "Roscoe" "orange"

Slide 30

Slide 30 text

Encoder KeyedEncodingContainer name color "Roscoe" "orange"

Slide 31

Slide 31 text

Encoder KeyedEncodingContainer name color "Roscoe" "orange" Compiler-generated defaults

Slide 32

Slide 32 text

Q: What if I want to modify properties as I encode them? { "name":"roscoe", "color":"orange" } { "name":"roscoe", "color":"orange" } e.g. Convert a string to lowercase?

Slide 33

Slide 33 text

Encoder KeyedEncodingContainer name color "roscoe" "orange"

Slide 34

Slide 34 text

What if I have custom types nested within other types?

Slide 35

Slide 35 text

Let's make things more complicated...

Slide 36

Slide 36 text

{ "name":"Kaitlin", "cats":[ { "name":"Chester", "color":"tan" }, { "name":"Roscoe", "color":"orange" } ] }

Slide 37

Slide 37 text

Encoder KeyedContainer name cats "Kaitlin" Unkeyed KeyedContainer name color "Chester" "tan" KeyedContainer name color "Roscoe" "orange" CatOwner cats cats[1] cats[0]

Slide 38

Slide 38 text

KeyedContainer name color "Roscoe" "orange" Unkeyed Encoder name cats "Kaitlin" KeyedContainer KeyedContainer name color "Chester" "tan"

Slide 39

Slide 39 text

KeyedContainer name color "Roscoe" "orange" Unkeyed Encoder name cats "Kaitlin" KeyedContainer KeyedContainer name color "Chester" "tan"

Slide 40

Slide 40 text

Encoding containers support storing three types of values. nil Bool, String, Double, Float all Int and UInt types Encodable type base case 1: nil base case 2: primitives recursive case

Slide 41

Slide 41 text

KeyedContainer name color "Roscoe" "orange" Unkeyed Encoder name cats "Kaitlin" KeyedContainer KeyedContainer name color "Chester" "tan" Calls Array.encode(to: self)

Slide 42

Slide 42 text

KeyedContainer name color "Roscoe" "orange" Unkeyed KeyedContainer name color "Chester" "tan" Encoder name cats "Kaitlin" KeyedContainer Array Cat Again, compiler and encoder do this for you!

Slide 43

Slide 43 text

{ "name":"Kaitlin", "cats":[ "Chester", "Roscoe" ] } "Chester" "Roscoe" UnkeyedContainer Encoder name cats "Kaitlin" KeyedContainer Q: What if I want to flatten my data?

Slide 44

Slide 44 text

{ "name":"Kaitlin", "cats":[ "Chester", "Roscoe" ] } "Chester" "Roscoe" UnkeyedContainer Encoder name cats "Kaitlin" KeyedContainer A: Single value containers Single value containers!

Slide 45

Slide 45 text

Flattening data + compiler generated CatOwner.encode no CodingKeys needed!

Slide 46

Slide 46 text

{ "name":"Kaitlin", "cats":[ "Chester", "Roscoe" ] } "Chester" "Roscoe" UnkeyedContainer Encoder name cats "Kaitlin" KeyedContainer Flattening data

Slide 47

Slide 47 text

Weren't we also talking about decoding?

Slide 48

Slide 48 text

Decoder KeyedDecodingContainer name color "Chester" "tan" Compiler generated defaults

Slide 49

Slide 49 text

Customization Takeaways ● Use CodingKeys to customize which properties are encoded/decoded, and what names they are encoded under and decoded from ● Use custom encode(to:) and init(from:) implementations to: ○ Transform data as you encode/decode it ○ Restructure your data

Slide 50

Slide 50 text

Super Advanced Usage: Writing Your Own Encoders and Decoders

Slide 51

Slide 51 text

Why doesn't the API match the Encodable protocol?

Slide 52

Slide 52 text

Encoder != Encoder

Slide 53

Slide 53 text

_JSONEncoder: Encoder JSONEncoder Why doesn't the API match the Encodable protocol? private Single encode method Container API More at https://tinyurl.com/encoder-protocol

Slide 54

Slide 54 text

JSONEncoder Structure _JSONEncoder: Encoder _JSONEncodingStorage JSONEncoder private

Slide 55

Slide 55 text

JSONEncoder Structure _JSONEncodingStorage NSDictionary or NSArray ● NSArray if first container requested is unkeyed ● NSDictionary otherwise ● Container API is used to construct it ● Why use NS*? ○ JSONSerialization requires it

Slide 56

Slide 56 text

Get top-level object from privateEncoder and pass it to JSONSerialization

Slide 57

Slide 57 text

Decoder != Decoder

Slide 58

Slide 58 text

JSONDecoder Structure _JSONDecoder: Decoder _JSONDecodingStorage JSONDecoder private

Slide 59

Slide 59 text

JSONDecoder Structure _JSONDecodingStorage NSDictionary or NSArray ● NSArray if JSON array was provided ● NSDictionary if JSON object was provided ● Container API is used to read from it ● Why use NS*? ○ JSONSerialization requires it

Slide 60

Slide 60 text

Use JSONSerialization to create object from data

Slide 61

Slide 61 text

Limitations ● Not very performant ○ See https://tinyurl.com/benchmark-codable ● Lots of boilerplate/error prone in some cases ○ What if I have 20 properties and only want to omit one?

Slide 62

Slide 62 text

Advantages ● The API makes Codable conformance trivial in many cases, but also allows for very advanced customization when needed. ● The standardized approach makes it so any Encodable type can be used with any Encoder, and any Decodable type can be used with any Decoder.

Slide 63

Slide 63 text

Thank you! Kaitlin Mahar Software Engineer @ MongoDB @k__mahar @kmahar