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
3: Swift Package Manager Basics
I’ve spent a lot of time in the past just dragging and dropping .swift files from one project to another. It works for a tiny project, but as soon as you have three different apps that all need the same logic—like a specific way of formatting dates or handling API errors—you end up with a synchronization nightmare. That's where the Swift Package Manager (SPM) comes in. It's not just about downloading libraries; it's about how we modularize our own code.
Setting up the sandbox
Let's say I want to build a TextAnalyzer tool. I want this to be a standalone package so I can use it in a command-line tool today and an iOS app tomorrow. Instead of manually creating folders, I'll let the tool do the heavy lifting. I'll open my terminal and run:
mkdir TextAnalyzer
cd TextAnalyzer
swift package init --type library
If you look at the file structure now, it's a bit opinionated. We have a Package.swift file at the root, and a Sources folder. I used to find this structure overkill for a small project, but once you start adding multiple targets, this consistency is what keeps you sane.
Wrestling with the Manifest
The Package.swift file is essentially the "brain" of the package. Let's open it. I see products and targets. I often confuse these two when I'm starting out, but here is how I think about it: a target is a collection of files that get compiled together; a product is what you actually "export" so other people (or your other projects) can use it.
I'll add a simple function to TextAnalyzer.swift inside the Sources folder to count words:
func countWords(in text: String) -> Int {
return text.split(separator: " ").count
}
Now, I'll try to use this in a separate test file or a main app. I hit a wall immediately: Cannot find 'countWords' in scope. Why? Because in Swift, the default access level is internal. Since this is a package intended for external use, the code is hidden from the outside world by default. I need to explicitly mark it as public.
public func countWords(in text: String) -> Int {
return text.split(separator: " ").count
}
Small detail, but it's the most common "why isn't this working" moment when building packages.
Bringing in a Dependency
My TextAnalyzer is a bit too simple. I want to do something more complex, like finding the most common word, and I know there are some great utility collections available in the swift-algorithms package. I don't want to rewrite those algorithms myself.
I'll head back to Package.swift. I need to tell SPM where to find this library and which version I want. I'll modify the dependencies array and the targets array like this:
dependencies: [
.package(url: "https://github.com/apple/swift-algorithms", from: "1.0.0"),
],
targets: [
.target(
name: "TextAnalyzer",
dependencies: [
.product(name: "Algorithms", package: "swift-algorithms"),
]),
]
I noticed something here: I had to add the package to the top-level dependencies list AND link the specific product to my TextAnalyzer target. If I forget the second part, the package downloads, but my code still can't see it. It's a two-step handshake.
Verifying the Build
Now I can finally use those algorithms in my code. I'll add import Algorithms at the top of my file. To make sure everything is wired up correctly without having to launch a full Xcode project, I'll just run this in the terminal:
swift build
If it finishes without an error, the manifest is correct, the dependencies are resolved, and my public API is ready to be consumed. It's a much cleaner workflow than manually managing framework search paths in an old project file.
📋 Practical Task
Build a "WeatherUnitConverter" Package
Your task is to create a reusable Swift Package that handles temperature conversions. This will practice the manifest configuration and access control we just covered.
- Initialize a new Swift library package named
WeatherUnitConverter. - Create a public function
celsiusToFahrenheit(celsius: Double) -> Doublethat performs the conversion. - Create a public function
fahrenheitToCelsius(fahrenheit: Double) -> Doublethat performs the conversion. - Ensure both functions are accessible from outside the module (remember the access modifier!).
- Run
swift buildin your terminal to verify the package compiles successfully.
There are no comments for now.