├── README.md
└── ProcessJSONData.playground
├── Contents.swift
├── timeline.xctimeline
├── playground.xcworkspace
├── contents.xcworkspacedata
└── xcuserdata
│ └── rongsun.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── contents.xcplayground
└── Pages
├── Read Data From JSOn.xcplaygroundpage
└── Contents.swift
├── Unmatched CodingKeys.xcplaygroundpage
└── Contents.swift
├── Nested Data.xcplaygroundpage
└── Contents.swift
└── Data with different depths.xcplaygroundpage
└── Contents.swift
/README.md:
--------------------------------------------------------------------------------
1 | # process-json-in-ios
--------------------------------------------------------------------------------
/ProcessJSONData.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | var greeting = "Hello, playground"
4 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/playground.xcworkspace/xcuserdata/rongsun.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ssr-404/process-json-in-ios/HEAD/ProcessJSONData.playground/playground.xcworkspace/xcuserdata/rongsun.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ProcessJSONData.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/Pages/Read Data From JSOn.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | let json1 = """
4 | [
5 | {
6 | "name": "Pokemon Violet",
7 | "price": 45,
8 | "description": "Pokémon Violet is one of the two mainline entries in the ninth generation of the Pokémon series, released alongside Pokémon Scarlet. Developed by Game Freak and published by Nintendo and The Pokémon Company, Pokémon Violet was released on November 18, 2022, for the Nintendo Switch."
9 | },
10 | {
11 | "name": "Resident Evil",
12 | "price": 90
13 | }
14 | ]
15 | """.data(using: .utf8)!
16 |
17 |
18 | struct Game: Codable {
19 | var name: String
20 | var price: Int
21 | var description: String?
22 | }
23 |
24 | let decoder = JSONDecoder()
25 | let games = try decoder.decode([Game].self, from: json1)
26 |
27 |
28 | print("The following games are available")
29 | for game in games {
30 | print("\(game.name): \(game.price) points \t")
31 | if let description = game.description {
32 | print("\(description)")
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/Pages/Unmatched CodingKeys.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | // In Swift, it's common for the property names you use in your code to differ from the corresponding keys in the JSON data. Despite this, you can still adhere to Swift's naming conventions for your properties while working with JSONEncoder and JSONDecoder.
2 | // To map Swift property names to different JSON keys, you use a nested enumeration named CodingKeys within the type that conforms to Codable, Encodable, or Decodable. The CodingKeys enum allows you to specify how each property in your Swift type corresponds to the keys in the JSON. This enum ensures that your code remains clean and follows Swift naming conventions, even when the JSON structure dictates different names. This technique is particularly useful when integrating with APIs where the JSON format is predetermined and cannot be altered to match your preferred Swift
3 |
4 | import Foundation
5 |
6 | let json2 = """
7 | [
8 | {
9 | "game_name": "Pokemon Violet",
10 | "game_price": 45,
11 | "description": "Pokémon Violet is one of the two mainline entries in the ninth generation of the Pokémon series, released alongside Pokémon Scarlet. Developed by Game Freak and published by Nintendo and The Pokémon Company, Pokémon Violet was released on November 18, 2022, for the Nintendo Switch."
12 | },
13 | {
14 | "game_name": "Resident Evil",
15 | "game_price": 90,
16 | "description": "Resident Evil is a media franchise created by Capcom, which began as a video game series in 1996. The series is set in a world where biological experiments by a pharmaceutical company called Umbrella Corporation lead to the creation of various types of zombies and monsters, setting the stage for horror-themed gameplay and storytelling."
17 | }
18 | ]
19 | """.data(using: .utf8)!
20 |
21 | struct Game: Codable {
22 | var name: String
23 | var price: Int
24 | var description: String?
25 |
26 | private enum CodingKeys: String, CodingKey {
27 | case name = "game_name"
28 | case price = "game_price"
29 | case description
30 | }
31 | }
32 |
33 | let decoder = JSONDecoder()
34 | let games = try decoder.decode([Game].self, from: json2)
35 |
36 | print("The following games are available: ")
37 | for game in games {
38 | print("\(game.name): \(game.price) \t")
39 | if let descipriont = game.description {
40 | print("\(descipriont) \t")
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/Pages/Nested Data.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | // When developing an app that handles JSON data from external or local sources, you might find that the structure of the JSON data does not align perfectly with the way you want to represent it in your app. Often, data that logically belongs together in your Swift code may be spread across multiple nested objects or arrays in the JSON.
2 |
3 | // To address these discrepancies, you can create a custom Decodable type that reflects the structure of the incoming JSON. This intermediate type allows you to safely decode the JSON data and serves as a reliable data source when initializing the types you will use throughout your application.
4 |
5 | // By employing these intermediate types, you maintain the flexibility to work with the most intuitive and natural types in your code, while still ensuring compatibility with a variety of JSON structures you might encounter.
6 |
7 | import Foundation
8 |
9 | struct Dealership {
10 | var name: String
11 | var cars: [Car]
12 |
13 | struct Car: Codable {
14 | var name: String
15 | var price: Int
16 | var description: String?
17 | }
18 | }
19 |
20 | let json3 = """
21 | [
22 | {
23 | "name": "Toyota Columbus",
24 | "branches": [
25 | {
26 | "name": "Toyota Columbus - Germain West",
27 | "departments": [
28 | {
29 | "name": "Certified Pre-Owned",
30 | "car": {
31 | "name": "GR Supra 2021",
32 | "price": 42000,
33 | "description": "a high-performance sports car developed by Toyota’s Gazoo Racing (GR) division."
34 | }
35 | }
36 | ]
37 | }
38 | ]
39 | },
40 | {
41 | "name": "Superdeal Auto Group",
42 | "branches": [
43 | {
44 | "name": "Superdeal Auto Group - Irvine",
45 | "departments": [
46 | {
47 | "name": "Seasonal Sale",
48 | "car": {
49 | "name": "Mustang 2018",
50 | "price": 32000,
51 | "description": "one of the most iconic American sports cars, known for its blend of performance, style, and cultural significance."
52 | }
53 | },
54 | {
55 | "name": "Luxury Cars",
56 | "car": {
57 | "name": "BMW X6 2023",
58 | "price": 72000,
59 | "description": "a luxury midsize SUV that combines sporty performance with a distinctive, coupe-like design. "
60 | }
61 | }
62 | ]
63 | }
64 | ]
65 | }
66 | ]
67 | """.data(using: .utf8)!
68 |
69 | struct DealerService: Decodable {
70 | var name: String
71 | var branches: [Branch]
72 |
73 | struct Branch: Decodable {
74 | var name: String
75 | var departments: [Department]
76 |
77 | struct Department: Decodable {
78 | var name: String
79 | var car: Dealership.Car
80 | }
81 | }
82 | }
83 |
84 | extension Dealership {
85 | init(from service: DealerService) {
86 | self.name = service.name
87 | self.cars = []
88 |
89 | for branch in service.branches {
90 | for department in branch.departments {
91 | cars.append(department.car)
92 | }
93 | }
94 | }
95 | }
96 |
97 | let services = try JSONDecoder().decode([DealerService].self, from: json3)
98 |
99 | let dealerships = services.map{ Dealership(from: $0) }
100 |
101 | for dealership in dealerships {
102 | print("\(dealership.name) is selling: ")
103 | for car in dealership.cars {
104 | print("\(car.name): &\(car.price)")
105 | if let description = car.description {
106 | print(description)
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/ProcessJSONData.playground/Pages/Data with different depths.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | // Sometimes, the data model provided by a JSON file or API may not align perfectly with the model you’re using in your app. In these cases, it may be necessary to merge or split JSON objects during the encoding and decoding processes. This often requires accessing various levels within the JSON object’s hierarchy to accurately map the data.
2 |
3 | // The example below demonstrates a common situation where such data merging is needed. It involves modeling a course list that tracks information such as the name, price, and other relevant details for each product it sells.
4 |
5 | import Foundation
6 |
7 | let json = """
8 | {
9 | "Intro to Network Security": {
10 | "credits": 4,
11 | "description": "Introduction to cybersecurity and digital authentication."
12 | },
13 | "Mobile Application - Android": {
14 | "credits": 3
15 | }
16 | }
17 | """.data(using: .utf8)!
18 |
19 | struct CourseList {
20 | var courses: [Course]
21 |
22 | struct Course {
23 | var name: String
24 | var credits: Int
25 | var description: String?
26 | }
27 | }
28 |
29 | extension CourseList: Encodable {
30 |
31 | struct CustomKey: CodingKey {
32 | var stringValue: String
33 | init?(stringValue: String) {
34 | self.stringValue = stringValue
35 | }
36 |
37 | var intValue: Int? { return nil }
38 | init?(intValue: Int) { return nil }
39 |
40 | static let credits = CustomKey(stringValue: "credits")!
41 | static let description = CustomKey(stringValue: "description")!
42 | }
43 |
44 | func encode(to encoder: Encoder) throws {
45 | var container = encoder.container(keyedBy: CustomKey.self)
46 |
47 | for course in courses {
48 | // Any course's `name` can be used as a key name.
49 | let nameKey = CustomKey(stringValue: course.name)!
50 | var courseContainer = container.nestedContainer(keyedBy: CustomKey.self, forKey: nameKey)
51 |
52 | // The rest of the keys use static names defined in `CustomKey`.
53 | try courseContainer.encode(course.credits, forKey: .credits)
54 | try courseContainer.encode(course.description, forKey: .description)
55 | }
56 | }
57 | }
58 |
59 | var encoder = JSONEncoder()
60 | encoder.outputFormatting = .prettyPrinted
61 |
62 | let courseList = CourseList(courses: [
63 | .init(name: "Intro to Network Security", credits: 4, description: "Introduction to cybersecurity and digital authentication."),
64 | .init(name: "Mobile Application - Android", credits: 3)
65 | ])
66 |
67 | print("The result of encoding a CourseList:")
68 | let encodedCourses = try encoder.encode(courseList)
69 | print(String(data: encodedCourses, encoding: .utf8)!)
70 | print()
71 |
72 | extension CourseList: Decodable {
73 | public init(from decoder: Decoder) throws {
74 | var courses = [Course]()
75 | let container = try decoder.container(keyedBy: CustomKey.self)
76 | for key in container.allKeys {
77 | // Note how the `key` in the loop above is used immediately to access a nested container.
78 | let courseContainer = try container.nestedContainer(keyedBy: CustomKey.self, forKey: key)
79 | let credits = try courseContainer.decode(Int.self, forKey: .credits)
80 | let description = try courseContainer.decodeIfPresent(String.self, forKey: .description)
81 |
82 | // The key is used again here and completes the collapse of the nesting that existed in the JSON representation.
83 | let course = Course(name: key.stringValue, credits: credits, description: description)
84 | courses.append(course)
85 | }
86 |
87 | self.init(courses: courses)
88 | }
89 | }
90 |
91 | let decoder = JSONDecoder()
92 | let decodedCourseList = try decoder.decode(CourseList.self, from: json)
93 |
94 | print("The University is opening the following courses:")
95 | for course in decodedCourseList.courses {
96 | print("\t\(course.name) (\(course.credits) credits)")
97 | if let description = course.description {
98 | print("\t\t\(description)")
99 | }
100 | }
101 |
--------------------------------------------------------------------------------