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
173: URL and URLComponents In Depth
You've probably used the URL(string:) initializer a thousand times. For a static link, it's perfect. But the moment you start building requests for a real-world API—where you have search terms, pagination, and API keys—trying to glue strings together is a recipe for a crash or a 400 Bad Request error. That's where URLComponents comes in.
Let's build a movie search URL generator for an API. We need to hit a specific endpoint, pass a search query, an API key, and an optional page number.
Starting with the basic URL
First, I'll define my base. I usually keep the base URL separate from the specific endpoint to keep things clean. I'm thinking about a standard REST structure here.
let baseURL = "https://api.themoviedb.org/3/search/movie"
let apiKey = "abc123xyz"
let searchTerm = "Guardians of the Galaxy"
The "Quick and Dirty" Mistake
When I first started out, I used to just interpolate everything into one big string. It feels faster, right? I'll show you what I mean, and why I stopped doing it.
// Don't do this in production
let badURLString = "\(baseURL)?api_key=\(apiKey)&query=\(searchTerm)"
if let url = URL(string: badURLString) {
print(url)
}
Here is the problem: searchTerm contains spaces. If you run this, you'll notice that URL(string:) might return nil, or the server will reject it because URLs cannot contain raw spaces. I could manually call addingPercentEncoding(withAllowedCharacters:) on every single variable, but that's tedious and error-prone. I'm basically doing the work the framework is already designed to do for me.
Structuring with URLComponents
Instead of treating the URL as a string, I'll treat it as a collection of components. URLComponents allows me to set the scheme, host, path, and query items independently. It handles all the percent-encoding automatically.
var components = URLComponents(string: baseURL)!
// I'm force-unwrapping here because I know my constant is valid.
components.queryItems = [
URLQueryItem(name: "api_key", value: apiKey),
URLQueryItem(name: "query", value: searchTerm)
]
if let finalURL = components.url {
print(finalURL)
// Output: https://api.themoviedb.org/3/search/movie?api_key=abc123xyz&query=Guardians%20of%20the%20Galaxy
}
See that %20? URLComponents saw the spaces in "Guardians of the Galaxy" and encoded them correctly without me writing a single line of regex or encoding logic.
Handling Optional Parameters Dynamically
In a real app, you don't always have every parameter. For example, you might only want to include the page parameter if the user has actually scrolled past the first page. Since queryItems is just an array of URLQueryItem, we can build it dynamically.
func buildSearchURL(query: String, page: Int?) -> URL? {
var components = URLComponents(string: "https://api.themoviedb.org/3/search/movie")!
var queryItems = [
URLQueryItem(name: "api_key", value: "abc123xyz"),
URLQueryItem(name: "query", value: query)
]
if let page = page {
queryItems.append(URLQueryItem(name: "page", value: String(page)))
}
components.queryItems = queryItems
return components.url
}
let urlWithPage = buildSearchURL(query: "Inception", page: 2)
let urlWithoutPage = buildSearchURL(query: "Inception", page: nil)
By separating the logic this way, I'm not fighting with & and ? symbols. I'm just managing a list of key-value pairs, and letting Swift handle the formatting. It's cleaner, it's safer, and it's how you should handle any dynamic URL construction.
📋 Practical Task
Build a Recipe Search URL Generator
You are integrating a recipe API that requires a base URL, a search term, a "cuisine" filter, and a "maxCalories" limit. However, the cuisine and calories filters are optional.
Write a function called createRecipeURL(searchTerm:cuisine:maxCalories:) that returns an optional URL. Use URLComponents and URLQueryItem to ensure the following:
- The base URL is
https://api.recipes.com/v1/search. - The
searchTermis always included. - The
cuisineparameter is only added to the URL if the provided string is not nil. - The
maxCaloriesparameter is only added if the provided integer is not nil. - All values are properly percent-encoded (handled automatically by your choice of components).
There are no comments for now.