Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
38: Encoding and Decoding with Codable
How do I actually turn a JSON string into a Swift object?
In the real world, you're almost always dealing with data coming from a web API. The "magic" happens through the Codable protocol, which is actually just a type alias for Encodable and Decodable. If you just want to read data, you only need Decodable, but usually, we just use Codable to keep things simple.
Let's say we're building a movie app. The API sends us a JSON object representing a film. To handle this, you create a struct that mirrors the structure of that JSON. I always recommend keeping your data models as simple structs.
struct Movie: Codable {
let title: String
let releaseYear: Int
let rating: Double
}
let json = """
{
"title": "Inception",
"releaseYear": 2010,
"rating": 8.8
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let movie = try decoder.decode(Movie.self, from: json)
print("Now we have a Swift object: \(movie.title)")
} catch {
print("Decoding failed: \(error)")
}
Notice the Movie.self part? You're telling the decoder exactly what type of object it should be trying to create from that blob of data. Also, don't skip the do-catch block. Decoding fails constantly in production because APIs change without warning, and if you use try!, your app will just crash.
What if the JSON keys don't match my Swift naming conventions?
This is where things usually get annoying. Most APIs use snake_case (like release_year), but in Swift, we use camelCase. You don't want to pollute your clean Swift code with underscores just to satisfy a legacy API.
You have two options here. If the entire API is consistent with snake_case, you can just tell the decoder to convert it automatically. It's a lifesaver.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Now "release_year" in JSON automatically maps to "releaseYear" in your struct
But what if the keys are completely different? Like if the API calls it film_title but you want your property to be called name? In that case, you use a CodingKeys enum. I've used this a thousand times to map weirdly named API fields to something that actually makes sense in my project.
struct Movie: Codable {
let name: String
let releaseYear: Int
enum CodingKeys: String, CodingKey {
case name = "film_title"
case releaseYear = "release_year"
}
}
The enum must be named exactly CodingKeys and conform to String and CodingKey. Swift will then use those mappings during the decoding process.
How do I stop my app from crashing when a field is missing?
I can't stress this enough: never trust an API. One day the rating field is there, and the next day the backend team decides it should be optional, and suddenly your decode call throws an error and your whole screen goes blank.
The solution is simple: use optionals. If a property in your struct is optional, JSONDecoder will simply set it to nil if the key is missing or the value is null in the JSON, rather than failing the entire decoding process.
struct Movie: Codable {
let title: String
let rating: Double? // This won't crash if "rating" is missing
let director: String? // Great for fields that aren't always provided
}
By making rating optional, you're telling Swift: "I'd like this value if it's there, but if it isn't, just keep moving." It's a much more resilient way to write your data layer. When you use the property later, you just handle the nil case with a default value or an if let statement.
📋 Practical Task
Exercise: Build a Weather Station Data Parser
You are receiving data from a remote weather station. Some of the sensors are occasionally offline, meaning some data points might be missing from the JSON payload. Your goal is to create a robust parser that doesn't crash when data is missing.
Requirements:
- Create a struct named
WeatherReadingthat conforms toCodable. - The JSON keys are
city_name,temperature, andwind_speed. - Map
city_nameto a Swift property calledcity. - Make
wind_speedoptional, as the wind sensor often fails. - Write a small piece of code that decodes the following JSON string:
"{"city_name": "Seattle", "temperature": 12.5}"(Note: wind_speed is missing!). - Print the city name and, if the wind speed exists, print it; otherwise, print "Wind data unavailable".
There are no comments for now.