SlideShare a Scribd company logo
JSON and Swift, Still A Better Love Story Than Twilight
Hi, I’m Donny.
I build iOS apps.
Hi, I’m Donny.
I build iOS apps.
JSON and Swift

JSON and Swift

Still A Better Love Story Than Twilight
Topics
‱ Pre-Swift 4.0 JSON handling

‱ Some things I learned about JSON pre-Swift 4.0

‱ Handling JSON in Swift 4.0

‱ Concluding remarks
Pre-Swift 4.0 JSON Handling
{
"items": [{
"line_up": [{
"artist": {
"name": "Foo"
}
}]
}]
}
JSON File:
Pre-Swift 4.0 JSON Handling
{
"items": [{
"line_up": [{
"artist": {
"name": "Foo"
}
}]
}]
}
JSON File: Swift 1.0:
if let items = json["items"] as? [[String: AnyObject]] {
if let ïŹrstItem = items.ïŹrst {
if let item = ïŹrstItem as? [String: AnyObject] {
if let lineUp = item["line_up"] as? [[String: AnyObject]] {
if let ïŹrstLineupItem = lineUp.ïŹrst {
if let artist = ïŹrstLineupItem["artist"] as? [String: AnyObject] {
if let name = artist["name"] as? String {
print(name)
}
}
}
}
}
}
}
Pre-Swift 4.0 JSON Handling
{
"items": [{
"line_up": [{
"artist": {
"name": "Foo"
}
}]
}]
}
JSON File: Swift 2.0:
guard let items = json["items"] as? [String: Any],
let ïŹrstItem = items.ïŹrst,
let item = ïŹrstItem as? [String: Any],
let lineUp = ïŹrstItem["line_up"] as? [[String: Any]],
let ïŹrstLineUpItem = lineUp.ïŹrst,
let artist = ïŹrstLineUpItem["artist"] as? [String: Any],
let name = artist["name"] as? String
else { return }
print(name)
Pre-Swift 4.0 JSON Handling
{
"items": [{
"line_up": [{
"artist": {
"name": "Foo"
}
}]
}]
}
JSON File: SwiftyJSON
guard let item = json["items"].arrayValue.ïŹrst,
let lineUpItem = item["line_up"].arrayValue.ïŹrst,
let name = lineUpItem["artist"]["name"].string
else { return }
print(name)
SwiftyJSON is amazing!
guard let item = json["items"].arrayValue.ïŹrst,
let lineUpItem = item["line_up"].arrayValue.ïŹrst,
let name = lineUpItem["artist"]["name"].string
else { return }
print(name)
😍
SwiftyJSON is amazing!
guard let item = json["items"].arrayValue.ïŹrst,
let lineUpItem = item["line_up"].arrayValue.ïŹrst,
let name = lineUpItem["artist"]["name"].string
else { return }
print(name)
😍
A performance test
A performance test
{ id: 260,
name: 'DAS Foute Oktoberfest 20171',
start: '2017-02-20T16:00:00+0000',
end: '2017-10-07T23:59:00+0000',
publish_from: '2016-11-09T08:00:00+0000',
publish_to: '2017-10-07T23:59:00+0000',
categories: [ { id: 2, name: 'NAME GOES HERE' } ],
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4!
1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532',
email: 'random@email.com',
id: 169,
radius: 500 },
line_up:
[ { id: 1, name: 'nino' },
{ id: 2, name: 'lisa' },
{ id: 7, name: 'kees' },
{ id: 4, name: 'Rodney' },
{ id: 8, name: 'Oscar' },
{ id: 9, name: 'Dick' } ],
description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen.
rnrnBinnenkort meer info!',
image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' }
A performance test
{ id: 260,
name: 'DAS Foute Oktoberfest 20171',
start: '2017-02-20T16:00:00+0000',
end: '2017-10-07T23:59:00+0000',
publish_from: '2016-11-09T08:00:00+0000',
publish_to: '2017-10-07T23:59:00+0000',
categories: [ { id: 2, name: 'NAME GOES HERE' } ],
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4!
1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532',
email: 'random@email.com',
id: 169,
radius: 500 },
line_up:
[ { id: 1, name: 'nino' },
{ id: 2, name: 'lisa' },
{ id: 7, name: 'kees' },
{ id: 4, name: 'Rodney' },
{ id: 8, name: 'Oscar' },
{ id: 9, name: 'Dick' } ],
description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen.
rnrnBinnenkort meer info!',
image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' }
‱ 2080 events
A performance test
{ id: 260,
name: 'DAS Foute Oktoberfest 20171',
start: '2017-02-20T16:00:00+0000',
end: '2017-10-07T23:59:00+0000',
publish_from: '2016-11-09T08:00:00+0000',
publish_to: '2017-10-07T23:59:00+0000',
categories: [ { id: 2, name: 'NAME GOES HERE' } ],
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4!
1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532',
email: 'random@email.com',
id: 169,
radius: 500 },
line_up:
[ { id: 1, name: 'nino' },
{ id: 2, name: 'lisa' },
{ id: 7, name: 'kees' },
{ id: 4, name: 'Rodney' },
{ id: 8, name: 'Oscar' },
{ id: 9, name: 'Dick' } ],
description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen.
rnrnBinnenkort meer info!',
image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' }
‱ 2080 events
‱ Nested objects
A performance test
{ id: 260,
name: 'DAS Foute Oktoberfest 20171',
start: '2017-02-20T16:00:00+0000',
end: '2017-10-07T23:59:00+0000',
publish_from: '2016-11-09T08:00:00+0000',
publish_to: '2017-10-07T23:59:00+0000',
categories: [ { id: 2, name: 'NAME GOES HERE' } ],
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4!
1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532',
email: 'random@email.com',
id: 169,
radius: 500 },
line_up:
[ { id: 1, name: 'nino' },
{ id: 2, name: 'lisa' },
{ id: 7, name: 'kees' },
{ id: 4, name: 'Rodney' },
{ id: 8, name: 'Oscar' },
{ id: 9, name: 'Dick' } ],
description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen.
rnrnBinnenkort meer info!',
image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' }
‱ 2080 events
‱ Nested objects
‱ Tested on iPhone 5c
A performance test
A performance test
func loadSwifty() {
guard let data = getFileData()
else { return }
let jsonFile = JSON(data: data)
for eventJSON in jsonFile["events"].arrayValue {
let location = Location(id: eventJSON["location"]["id"].intValue,
name: eventJSON["location"]["name"].stringValue,
lat: eventJSON["location"]["latitude"].double,
lon: eventJSON[“location"]["longitude"].double,
address: eventJSON["location"]["address"].stringValue,
zipcode: eventJSON[“location"]["zipcode"].stringValue)
// etc.
}
showCompletion()
}
A performance test
func loadSwifty() {
guard let data = getFileData()
else { return }
let jsonFile = JSON(data: data)
for eventJSON in jsonFile["events"].arrayValue {
let location = Location(id: eventJSON["location"]["id"].intValue,
name: eventJSON["location"]["name"].stringValue,
lat: eventJSON["location"]["latitude"].double,
lon: eventJSON[“location"]["longitude"].double,
address: eventJSON["location"]["address"].stringValue,
zipcode: eventJSON[“location"]["zipcode"].stringValue)
// etc.
}
showCompletion()
}
6.76 seconds to complete
A performance test
func loadSwifty() {
guard let data = getFileData()
else { return }
let jsonFile = JSON(data: data)
for eventJSON in jsonFile["events"].arrayValue {
let location = Location(id: eventJSON["location"]["id"].intValue,
name: eventJSON["location"]["name"].stringValue,
lat: eventJSON["location"]["latitude"].double,
lon: eventJSON[“location"]["longitude"].double,
address: eventJSON["location"]["address"].stringValue,
zipcode: eventJSON[“location"]["zipcode"].stringValue)
// etc.
}
showCompletion()
}
6.76 seconds to complete
"😭
A performance test
A performance test
func loadNormal() {
guard let data = getFileData(),
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let json = jsonObject as? DWJSON,
let events = json["events"] as? [DWJSON]
else { return }
for eventJSON in events {
guard let locationJSON = eventJSON["location"] as? DWJSON,
let categoriesJSON = eventJSON["categories"] as? [DWJSON],
let lineUpJSON = eventJSON["line_up"] as? [DWJSON]
else { return }
// etc.
}
showCompletion()
}
A performance test
func loadNormal() {
guard let data = getFileData(),
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let json = jsonObject as? DWJSON,
let events = json["events"] as? [DWJSON]
else { return }
for eventJSON in events {
guard let locationJSON = eventJSON["location"] as? DWJSON,
let categoriesJSON = eventJSON["categories"] as? [DWJSON],
let lineUpJSON = eventJSON["line_up"] as? [DWJSON]
else { return }
// etc.
}
showCompletion()
}
1.84 seconds to complete
A performance test
func loadNormal() {
guard let data = getFileData(),
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let json = jsonObject as? DWJSON,
let events = json["events"] as? [DWJSON]
else { return }
for eventJSON in events {
guard let locationJSON = eventJSON["location"] as? DWJSON,
let categoriesJSON = eventJSON["categories"] as? [DWJSON],
let lineUpJSON = eventJSON["line_up"] as? [DWJSON]
else { return }
// etc.
}
showCompletion()
}
1.84 seconds to complete
$😍
What did I learn?
What did I learn?
‱ Be careful about libraries that make things easy; they might be (very) slow.
What did I learn?
‱ Be careful about libraries that make things easy; they might be (very) slow.
‱ Sometimes uglier code is faster (unfortunately).
What did I learn?
‱ Be careful about libraries that make things easy; they might be (very) slow.
‱ Sometimes uglier code is faster (unfortunately).
‱ Working with JSON isn’t as bad as it seems in Swift 2.0+.
What about Swift 4.0?
❓&❓
Introducing Codable
Introducing Codable
struct Category {
let id: Int
let name: String
}
let category = Category(id: categoryJSON["id"] as? Int ?? 0,
name: categoryJSON["name"] as? String ?? "")
< Swift 4.0
Introducing Codable
struct Category {
let id: Int
let name: String
}
let category = Category(id: categoryJSON["id"] as? Int ?? 0,
name: categoryJSON["name"] as? String ?? "")
< Swift 4.0
struct Category: Codable {
let id: Int
let name: String
}
Swift 4.0
let decoder = JSONDecoder()
let category = try? decoder.decode(Category.self, from: data)
Introducing Codable
struct Category {
let id: Int
let name: String
}
let category = Category(id: categoryJSON["id"] as? Int ?? 0,
name: categoryJSON["name"] as? String ?? "")
< Swift 4.0
struct Category: Codable {
let id: Int
let name: String
}
Swift 4.0
let decoder = JSONDecoder()
let category = try? decoder.decode(Category.self, from: data)
Property names are directly mapped to JSON keys
Introducing Codable
But what if the keys don’t match?
Introducing Codable
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: ‘https://www.google.nl/',
email: 'random@email.com',
id: 169,
radius: 500 }
But what if the keys don’t match?
Introducing Codable
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: ‘https://www.google.nl/',
email: 'random@email.com',
id: 169,
radius: 500 }
But what if the keys don’t match?
Introducing Codable
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: ‘https://www.google.nl/',
email: 'random@email.com',
id: 169,
radius: 500 }
struct Location {
let id: Int
let name: String
let lat: Double?
let lon: Double?
let address: String
let zipcode: String
}
But what if the keys don’t match?
Introducing Codable
location:
{ name: 'Goffertpark, Nijmegen (NL)',
address: '',
zipcode: '1234AB',
city: 'Nijmegen',
state: 'Noord-whateverland',
country: 'NL',
latitude: 51.82548,
longitude: 5.836629,
url: ‘https://www.google.nl/',
email: 'random@email.com',
id: 169,
radius: 500 }
struct Location: Codable {
enum CodingKeys: String, CodingKey {
case id, name, address, zipcode
case lat = "latitude"
case lon = "longitude"
}
let id: Int
let name: String
let lat: Double?
let lon: Double?
let address: String
let zipcode: String
}
But what if the keys don’t match?
Codable Performance
Codable Performance
func loadCodable() {
guard let data = getFileData()
else { return }
do {
let decoder = JSONDecoder()
let eventsResponse = try decoder.decode(EventsResponse.self, from: data)
showCompletion()
} catch {
print(error)
}
}
Codable Performance
func loadCodable() {
guard let data = getFileData()
else { return }
do {
let decoder = JSONDecoder()
let eventsResponse = try decoder.decode(EventsResponse.self, from: data)
showCompletion()
} catch {
print(error)
}
}
1.82 seconds to complete
Codable Performance
func loadCodable() {
guard let data = getFileData()
else { return }
do {
let decoder = JSONDecoder()
let eventsResponse = try decoder.decode(EventsResponse.self, from: data)
showCompletion()
} catch {
print(error)
}
}
1.82 seconds to complete
$😍
Codable and Date
struct DWDate: Codable {
let date: Date?
}
let jsonString = "{"date": "31-08-2017 +0000"}"
let json = jsonString.data(using: .utf8)!
do {
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy Z"
decoder.dateDecodingStrategy = .formatted(formatter)
let response = try decoder.decode(DWDate.self, from: json)
} catch {
print(error)
}
Codable and Date
struct DWDate: Codable {
let date: Date?
}
let jsonString = "{"date": ""}" //' ' '
let json = jsonString.data(using: .utf8)!
do {
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy Z"
decoder.dateDecodingStrategy = .formatted(formatter)
let response = try decoder.decode(DWDate.self, from: json)
} catch {
print(error)
}
Codable and Date
struct DWDate: Codable {
let date: Date?
}
let jsonString = "{"date": ""}" //' ' '
let json = jsonString.data(using: .utf8)!
do {
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy Z"
decoder.dateDecodingStrategy = .formatted(formatter)
let response = try decoder.decode(DWDate.self, from: json)
} catch {
print(error)
}
dataCorrupted(Swift.DecodingError.Context(codingPath:
[__lldb_expr_156.DWDate.(CodingKeys in
_995AD5D014A8F9E1965F4BEEB81F4E38).date], debugDescription: "Date
string does not match format expected by formatter.", underlyingError: nil))
Codable and Date
struct DWDate: Codable {
let date: Date?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.date = try? container.decode(Date.self, forKey: .date)
}
}
What else should you know?
struct Prize: Codable {
enum PrizeType: Int, Codable {
case ticket = 1, voucher = 2
}
let name: String
let type: PrizeType
}
{
"name" : "Test",
"type" : 1
}
What else should you know?
let prize = Prize(name: "Test", type: .ticket)
let encoder = JSONEncoder()
let result = try! encoder.encode(prize)
{
"name" : "Test",
"type" : 1
}
What else should you know?
struct EventsResponse: Codable {
let events: [Event]
}
let eventsResponse = try decoder.decode([String: [Event]].self, from: data)
let eventsResponse = try decoder.decode(EventsResponse.self, from: data)
Concluding remarks
Concluding remarks
‱ Handling JSON with libraries can be convenient yet slow
Concluding remarks
‱ Handling JSON with libraries can be convenient yet slow
‱ The Codable protocol performs really well
Concluding remarks
‱ Handling JSON with libraries can be convenient yet slow
‱ The Codable protocol performs really well
‱ Optional date handling is a bit too strict IMO
Concluding remarks
‱ Handling JSON with libraries can be convenient yet slow
‱ The Codable protocol performs really well
‱ Optional date handling is a bit too strict IMO
‱ You can do really powerful things with Codable
Concluding remarks
‱ Handling JSON with libraries can be convenient yet slow
‱ The Codable protocol performs really well
‱ Optional date handling is a bit too strict IMO
‱ You can do really powerful things with Codable
http://benscheirman.com/2017/06/ultimate-guide-to-json-parsing-with-swift-4/
Thanks!

More Related Content

PDF
The Testing Games: Mocking, yay!
PDF
Server Side Events
PDF
async/await in Swift
PDF
Improving apps with iOS 10 notifications (do iOS 2016)
KEY
Async. and Realtime Geo Applications with Node.js
PPTX
Realm or: How I learned to stop worrying and love my app database
PDF
Web Integration Patterns in the Era of HTML5
PDF
The (unknown) collections module
The Testing Games: Mocking, yay!
Server Side Events
async/await in Swift
Improving apps with iOS 10 notifications (do iOS 2016)
Async. and Realtime Geo Applications with Node.js
Realm or: How I learned to stop worrying and love my app database
Web Integration Patterns in the Era of HTML5
The (unknown) collections module

What's hot (18)

PDF
PostgreSQL Open SV 2018
PPTX
JQuery
PDF
Design how your objects talk through mocking
PDF
jQuery%20on%20Rails%20Presentation
PDF
Decoupling with Design Patterns and Symfony2 DIC
PDF
Getting physical with web bluetooth in the browser
PPTX
Getting started with Elasticsearch and .NET
PDF
Symfony & Javascript. Combining the best of two worlds
PDF
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
PDF
Url programming
PDF
Is HTML5 Ready? (workshop)
PDF
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
PDF
Guard Authentication: Powerful, Beautiful Security
PDF
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
PDF
The Ring programming language version 1.5.2 book - Part 39 of 181
PDF
The IoC Hydra
PDF
Beyond the DOM: Sane Structure for JS Apps
PPTX
Php sql-android
PostgreSQL Open SV 2018
JQuery
Design how your objects talk through mocking
jQuery%20on%20Rails%20Presentation
Decoupling with Design Patterns and Symfony2 DIC
Getting physical with web bluetooth in the browser
Getting started with Elasticsearch and .NET
Symfony & Javascript. Combining the best of two worlds
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
Url programming
Is HTML5 Ready? (workshop)
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Guard Authentication: Powerful, Beautiful Security
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
The Ring programming language version 1.5.2 book - Part 39 of 181
The IoC Hydra
Beyond the DOM: Sane Structure for JS Apps
Php sql-android
Ad

More from Donny Wals (13)

PDF
Your 🧠 on Swift Concurrency
PDF
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
PDF
The combine triad
PDF
Building reusable components with generics and protocols
PDF
Adopting tdd in the workplace
PDF
Me and my importers
PDF
Adopting tdd in the workplace
PDF
In Defense Of Core Data
PDF
Effectively Producing And Shipping Frameworks For Multiple Platforms
PDF
Talk - git task managers and ci
PDF
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
PDF
Marketing strategie Arto
KEY
Hoorcollege Flash 9-2-2012
Your 🧠 on Swift Concurrency
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
The combine triad
Building reusable components with generics and protocols
Adopting tdd in the workplace
Me and my importers
Adopting tdd in the workplace
In Defense Of Core Data
Effectively Producing And Shipping Frameworks For Multiple Platforms
Talk - git task managers and ci
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
Marketing strategie Arto
Hoorcollege Flash 9-2-2012
Ad

Recently uploaded (20)

PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PDF
Digital Strategies for Manufacturing Companies
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Understanding Forklifts - TECH EHS Solution
PDF
medical staffing services at VALiNTRY
PPTX
Odoo POS Development Services by CandidRoot Solutions
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
 
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PPTX
ai tools demonstartion for schools and inter college
PPTX
ISO 45001 Occupational Health and Safety Management System
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Digital Strategies for Manufacturing Companies
Wondershare Filmora 15 Crack With Activation Key [2025
PTS Company Brochure 2025 (1).pdf.......
Understanding Forklifts - TECH EHS Solution
medical staffing services at VALiNTRY
Odoo POS Development Services by CandidRoot Solutions
Upgrade and Innovation Strategies for SAP ERP Customers
Navsoft: AI-Powered Business Solutions & Custom Software Development
Softaken Excel to vCard Converter Software.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
 
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
ai tools demonstartion for schools and inter college
ISO 45001 Occupational Health and Safety Management System
Design an Analysis of Algorithms II-SECS-1021-03
Odoo Companies in India – Driving Business Transformation.pdf
How to Migrate SBCGlobal Email to Yahoo Easily
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool

JSON and Swift, Still A Better Love Story Than Twilight

  • 2. Hi, I’m Donny. I build iOS apps.
  • 3. Hi, I’m Donny. I build iOS apps.
  • 5. JSON and Swift
 Still A Better Love Story Than Twilight
  • 6. Topics ‱ Pre-Swift 4.0 JSON handling ‱ Some things I learned about JSON pre-Swift 4.0 ‱ Handling JSON in Swift 4.0 ‱ Concluding remarks
  • 7. Pre-Swift 4.0 JSON Handling { "items": [{ "line_up": [{ "artist": { "name": "Foo" } }] }] } JSON File:
  • 8. Pre-Swift 4.0 JSON Handling { "items": [{ "line_up": [{ "artist": { "name": "Foo" } }] }] } JSON File: Swift 1.0: if let items = json["items"] as? [[String: AnyObject]] { if let ïŹrstItem = items.ïŹrst { if let item = ïŹrstItem as? [String: AnyObject] { if let lineUp = item["line_up"] as? [[String: AnyObject]] { if let ïŹrstLineupItem = lineUp.ïŹrst { if let artist = ïŹrstLineupItem["artist"] as? [String: AnyObject] { if let name = artist["name"] as? String { print(name) } } } } } } }
  • 9. Pre-Swift 4.0 JSON Handling { "items": [{ "line_up": [{ "artist": { "name": "Foo" } }] }] } JSON File: Swift 2.0: guard let items = json["items"] as? [String: Any], let ïŹrstItem = items.ïŹrst, let item = ïŹrstItem as? [String: Any], let lineUp = ïŹrstItem["line_up"] as? [[String: Any]], let ïŹrstLineUpItem = lineUp.ïŹrst, let artist = ïŹrstLineUpItem["artist"] as? [String: Any], let name = artist["name"] as? String else { return } print(name)
  • 10. Pre-Swift 4.0 JSON Handling { "items": [{ "line_up": [{ "artist": { "name": "Foo" } }] }] } JSON File: SwiftyJSON guard let item = json["items"].arrayValue.ïŹrst, let lineUpItem = item["line_up"].arrayValue.ïŹrst, let name = lineUpItem["artist"]["name"].string else { return } print(name)
  • 11. SwiftyJSON is amazing! guard let item = json["items"].arrayValue.ïŹrst, let lineUpItem = item["line_up"].arrayValue.ïŹrst, let name = lineUpItem["artist"]["name"].string else { return } print(name) 😍
  • 12. SwiftyJSON is amazing! guard let item = json["items"].arrayValue.ïŹrst, let lineUpItem = item["line_up"].arrayValue.ïŹrst, let name = lineUpItem["artist"]["name"].string else { return } print(name) 😍
  • 14. A performance test { id: 260, name: 'DAS Foute Oktoberfest 20171', start: '2017-02-20T16:00:00+0000', end: '2017-10-07T23:59:00+0000', publish_from: '2016-11-09T08:00:00+0000', publish_to: '2017-10-07T23:59:00+0000', categories: [ { id: 2, name: 'NAME GOES HERE' } ], location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4! 1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532', email: 'random@email.com', id: 169, radius: 500 }, line_up: [ { id: 1, name: 'nino' }, { id: 2, name: 'lisa' }, { id: 7, name: 'kees' }, { id: 4, name: 'Rodney' }, { id: 8, name: 'Oscar' }, { id: 9, name: 'Dick' } ], description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen. rnrnBinnenkort meer info!', image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' }
  • 15. A performance test { id: 260, name: 'DAS Foute Oktoberfest 20171', start: '2017-02-20T16:00:00+0000', end: '2017-10-07T23:59:00+0000', publish_from: '2016-11-09T08:00:00+0000', publish_to: '2017-10-07T23:59:00+0000', categories: [ { id: 2, name: 'NAME GOES HERE' } ], location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4! 1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532', email: 'random@email.com', id: 169, radius: 500 }, line_up: [ { id: 1, name: 'nino' }, { id: 2, name: 'lisa' }, { id: 7, name: 'kees' }, { id: 4, name: 'Rodney' }, { id: 8, name: 'Oscar' }, { id: 9, name: 'Dick' } ], description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen. rnrnBinnenkort meer info!', image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' } ‱ 2080 events
  • 16. A performance test { id: 260, name: 'DAS Foute Oktoberfest 20171', start: '2017-02-20T16:00:00+0000', end: '2017-10-07T23:59:00+0000', publish_from: '2016-11-09T08:00:00+0000', publish_to: '2017-10-07T23:59:00+0000', categories: [ { id: 2, name: 'NAME GOES HERE' } ], location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4! 1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532', email: 'random@email.com', id: 169, radius: 500 }, line_up: [ { id: 1, name: 'nino' }, { id: 2, name: 'lisa' }, { id: 7, name: 'kees' }, { id: 4, name: 'Rodney' }, { id: 8, name: 'Oscar' }, { id: 9, name: 'Dick' } ], description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen. rnrnBinnenkort meer info!', image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' } ‱ 2080 events ‱ Nested objects
  • 17. A performance test { id: 260, name: 'DAS Foute Oktoberfest 20171', start: '2017-02-20T16:00:00+0000', end: '2017-10-07T23:59:00+0000', publish_from: '2016-11-09T08:00:00+0000', publish_to: '2017-10-07T23:59:00+0000', categories: [ { id: 2, name: 'NAME GOES HERE' } ], location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: 'https://www.google.nl/maps/place/Goffertpark/@52.2576689,5.2644167,8.25z/data=!4m5!3m4! 1s0x47c7089b2ce7dff1:0xfd37f35c5b91b9c8!8m2!3d51.8255586!4d5.8366532', email: 'random@email.com', id: 169, radius: 500 }, line_up: [ { id: 1, name: 'nino' }, { id: 2, name: 'lisa' }, { id: 7, name: 'kees' }, { id: 4, name: 'Rodney' }, { id: 8, name: 'Oscar' }, { id: 9, name: 'Dick' } ], description: 'Matrixx presenteert Het Foute Oktoberfest 2017!rnrnZaterdag 7 oktober in een grote feesttent op de Goffertweide in Nijmegen. rnrnBinnenkort meer info!', image_url: 'http://app.appimin.com/ïŹlelib/storage/events/58246fa620305_het-foute-oktoberfest-2017.jpg' } ‱ 2080 events ‱ Nested objects ‱ Tested on iPhone 5c
  • 19. A performance test func loadSwifty() { guard let data = getFileData() else { return } let jsonFile = JSON(data: data) for eventJSON in jsonFile["events"].arrayValue { let location = Location(id: eventJSON["location"]["id"].intValue, name: eventJSON["location"]["name"].stringValue, lat: eventJSON["location"]["latitude"].double, lon: eventJSON[“location"]["longitude"].double, address: eventJSON["location"]["address"].stringValue, zipcode: eventJSON[“location"]["zipcode"].stringValue) // etc. } showCompletion() }
  • 20. A performance test func loadSwifty() { guard let data = getFileData() else { return } let jsonFile = JSON(data: data) for eventJSON in jsonFile["events"].arrayValue { let location = Location(id: eventJSON["location"]["id"].intValue, name: eventJSON["location"]["name"].stringValue, lat: eventJSON["location"]["latitude"].double, lon: eventJSON[“location"]["longitude"].double, address: eventJSON["location"]["address"].stringValue, zipcode: eventJSON[“location"]["zipcode"].stringValue) // etc. } showCompletion() } 6.76 seconds to complete
  • 21. A performance test func loadSwifty() { guard let data = getFileData() else { return } let jsonFile = JSON(data: data) for eventJSON in jsonFile["events"].arrayValue { let location = Location(id: eventJSON["location"]["id"].intValue, name: eventJSON["location"]["name"].stringValue, lat: eventJSON["location"]["latitude"].double, lon: eventJSON[“location"]["longitude"].double, address: eventJSON["location"]["address"].stringValue, zipcode: eventJSON[“location"]["zipcode"].stringValue) // etc. } showCompletion() } 6.76 seconds to complete "😭
  • 23. A performance test func loadNormal() { guard let data = getFileData(), let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []), let json = jsonObject as? DWJSON, let events = json["events"] as? [DWJSON] else { return } for eventJSON in events { guard let locationJSON = eventJSON["location"] as? DWJSON, let categoriesJSON = eventJSON["categories"] as? [DWJSON], let lineUpJSON = eventJSON["line_up"] as? [DWJSON] else { return } // etc. } showCompletion() }
  • 24. A performance test func loadNormal() { guard let data = getFileData(), let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []), let json = jsonObject as? DWJSON, let events = json["events"] as? [DWJSON] else { return } for eventJSON in events { guard let locationJSON = eventJSON["location"] as? DWJSON, let categoriesJSON = eventJSON["categories"] as? [DWJSON], let lineUpJSON = eventJSON["line_up"] as? [DWJSON] else { return } // etc. } showCompletion() } 1.84 seconds to complete
  • 25. A performance test func loadNormal() { guard let data = getFileData(), let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []), let json = jsonObject as? DWJSON, let events = json["events"] as? [DWJSON] else { return } for eventJSON in events { guard let locationJSON = eventJSON["location"] as? DWJSON, let categoriesJSON = eventJSON["categories"] as? [DWJSON], let lineUpJSON = eventJSON["line_up"] as? [DWJSON] else { return } // etc. } showCompletion() } 1.84 seconds to complete $😍
  • 26. What did I learn?
  • 27. What did I learn? ‱ Be careful about libraries that make things easy; they might be (very) slow.
  • 28. What did I learn? ‱ Be careful about libraries that make things easy; they might be (very) slow. ‱ Sometimes uglier code is faster (unfortunately).
  • 29. What did I learn? ‱ Be careful about libraries that make things easy; they might be (very) slow. ‱ Sometimes uglier code is faster (unfortunately). ‱ Working with JSON isn’t as bad as it seems in Swift 2.0+.
  • 30. What about Swift 4.0? ❓&❓
  • 32. Introducing Codable struct Category { let id: Int let name: String } let category = Category(id: categoryJSON["id"] as? Int ?? 0, name: categoryJSON["name"] as? String ?? "") < Swift 4.0
  • 33. Introducing Codable struct Category { let id: Int let name: String } let category = Category(id: categoryJSON["id"] as? Int ?? 0, name: categoryJSON["name"] as? String ?? "") < Swift 4.0 struct Category: Codable { let id: Int let name: String } Swift 4.0 let decoder = JSONDecoder() let category = try? decoder.decode(Category.self, from: data)
  • 34. Introducing Codable struct Category { let id: Int let name: String } let category = Category(id: categoryJSON["id"] as? Int ?? 0, name: categoryJSON["name"] as? String ?? "") < Swift 4.0 struct Category: Codable { let id: Int let name: String } Swift 4.0 let decoder = JSONDecoder() let category = try? decoder.decode(Category.self, from: data) Property names are directly mapped to JSON keys
  • 35. Introducing Codable But what if the keys don’t match?
  • 36. Introducing Codable location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: ‘https://www.google.nl/', email: 'random@email.com', id: 169, radius: 500 } But what if the keys don’t match?
  • 37. Introducing Codable location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: ‘https://www.google.nl/', email: 'random@email.com', id: 169, radius: 500 } But what if the keys don’t match?
  • 38. Introducing Codable location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: ‘https://www.google.nl/', email: 'random@email.com', id: 169, radius: 500 } struct Location { let id: Int let name: String let lat: Double? let lon: Double? let address: String let zipcode: String } But what if the keys don’t match?
  • 39. Introducing Codable location: { name: 'Goffertpark, Nijmegen (NL)', address: '', zipcode: '1234AB', city: 'Nijmegen', state: 'Noord-whateverland', country: 'NL', latitude: 51.82548, longitude: 5.836629, url: ‘https://www.google.nl/', email: 'random@email.com', id: 169, radius: 500 } struct Location: Codable { enum CodingKeys: String, CodingKey { case id, name, address, zipcode case lat = "latitude" case lon = "longitude" } let id: Int let name: String let lat: Double? let lon: Double? let address: String let zipcode: String } But what if the keys don’t match?
  • 41. Codable Performance func loadCodable() { guard let data = getFileData() else { return } do { let decoder = JSONDecoder() let eventsResponse = try decoder.decode(EventsResponse.self, from: data) showCompletion() } catch { print(error) } }
  • 42. Codable Performance func loadCodable() { guard let data = getFileData() else { return } do { let decoder = JSONDecoder() let eventsResponse = try decoder.decode(EventsResponse.self, from: data) showCompletion() } catch { print(error) } } 1.82 seconds to complete
  • 43. Codable Performance func loadCodable() { guard let data = getFileData() else { return } do { let decoder = JSONDecoder() let eventsResponse = try decoder.decode(EventsResponse.self, from: data) showCompletion() } catch { print(error) } } 1.82 seconds to complete $😍
  • 44. Codable and Date struct DWDate: Codable { let date: Date? } let jsonString = "{"date": "31-08-2017 +0000"}" let json = jsonString.data(using: .utf8)! do { let decoder = JSONDecoder() let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy Z" decoder.dateDecodingStrategy = .formatted(formatter) let response = try decoder.decode(DWDate.self, from: json) } catch { print(error) }
  • 45. Codable and Date struct DWDate: Codable { let date: Date? } let jsonString = "{"date": ""}" //' ' ' let json = jsonString.data(using: .utf8)! do { let decoder = JSONDecoder() let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy Z" decoder.dateDecodingStrategy = .formatted(formatter) let response = try decoder.decode(DWDate.self, from: json) } catch { print(error) }
  • 46. Codable and Date struct DWDate: Codable { let date: Date? } let jsonString = "{"date": ""}" //' ' ' let json = jsonString.data(using: .utf8)! do { let decoder = JSONDecoder() let formatter = DateFormatter() formatter.dateFormat = "dd-MM-yyyy Z" decoder.dateDecodingStrategy = .formatted(formatter) let response = try decoder.decode(DWDate.self, from: json) } catch { print(error) } dataCorrupted(Swift.DecodingError.Context(codingPath: [__lldb_expr_156.DWDate.(CodingKeys in _995AD5D014A8F9E1965F4BEEB81F4E38).date], debugDescription: "Date string does not match format expected by formatter.", underlyingError: nil))
  • 47. Codable and Date struct DWDate: Codable { let date: Date? init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.date = try? container.decode(Date.self, forKey: .date) } }
  • 48. What else should you know? struct Prize: Codable { enum PrizeType: Int, Codable { case ticket = 1, voucher = 2 } let name: String let type: PrizeType } { "name" : "Test", "type" : 1 }
  • 49. What else should you know? let prize = Prize(name: "Test", type: .ticket) let encoder = JSONEncoder() let result = try! encoder.encode(prize) { "name" : "Test", "type" : 1 }
  • 50. What else should you know? struct EventsResponse: Codable { let events: [Event] } let eventsResponse = try decoder.decode([String: [Event]].self, from: data) let eventsResponse = try decoder.decode(EventsResponse.self, from: data)
  • 52. Concluding remarks ‱ Handling JSON with libraries can be convenient yet slow
  • 53. Concluding remarks ‱ Handling JSON with libraries can be convenient yet slow ‱ The Codable protocol performs really well
  • 54. Concluding remarks ‱ Handling JSON with libraries can be convenient yet slow ‱ The Codable protocol performs really well ‱ Optional date handling is a bit too strict IMO
  • 55. Concluding remarks ‱ Handling JSON with libraries can be convenient yet slow ‱ The Codable protocol performs really well ‱ Optional date handling is a bit too strict IMO ‱ You can do really powerful things with Codable
  • 56. Concluding remarks ‱ Handling JSON with libraries can be convenient yet slow ‱ The Codable protocol performs really well ‱ Optional date handling is a bit too strict IMO ‱ You can do really powerful things with Codable http://benscheirman.com/2017/06/ultimate-guide-to-json-parsing-with-swift-4/