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
49: Swift Package Manager for Libraries
Think of creating a Swift library like building a specialized tool kit. If you're a carpenter, you don't build a new hammer and a new saw from scratch every single time you start a new project. Instead, you have a dedicated toolkit that you carry from job to job. You don't necessarily care how the hammer was forged; you just care that it works and that it's available whenever you open that box.
In Swift, a Package is that toolkit. Instead of copying and pasting a handful of helper classes from one app project to another, you wrap that logic into a Swift Package. The Package.swift file is essentially the "manifest" or the inventory list of your toolkit—it tells Swift what's inside, what other toolkits are needed to make it work, and which tools are actually available for the end user to grab.
Defining Your Toolkit in Package.swift
When you create a new library package, the heart of everything is the Package.swift file. I've seen a lot of developers get intimidated by this file because it looks like a configuration script, but it's actually just Swift code. You're defining a Package object.
Let's say we're building a library called ColorPalette that helps developers generate complementary colors for their UI. Your manifest would look something like this:
import PackageDescription
let package = Package(
name: "ColorPalette",
products: [
// This is what the 'customer' sees.
// We're exporting the library so other projects can import it.
.library(name: "ColorPalette", targets: ["ColorPalette"]),
],
dependencies: [
// If our color library needed another library (like a math utility),
// we'd list it here. For now, we're keeping it lean.
],
targets: [
// This is where the actual code lives.
.target(name: "ColorPalette", dependencies: []),
.testTarget(name: "ColorPaletteTests", dependencies: ["ColorPalette"]),
]
)
Notice the distinction between products and targets. A target is a collection of files that get compiled together. A product is how you "package" those targets for others to use. You might have five different targets for internal organization, but you only expose one single library product to the public.
The "Public" Hurdle
Here is the most common mistake I see when people move from app development to library development: forgetting the public keyword.
In a standard app project, most of your classes and functions are internal by default. That works fine because everything is in one big bucket. But libraries are different. If you write a great function in your library but don't explicitly mark it as public, the person importing your library won't be able to see it. It's like putting a tool in your kit but locking it inside a safe that only you have the key to.
If our ColorPalette library looks like this, it's useless to anyone else:
struct PaletteGenerator {
func generateComplementary(to color: String) -> String {
return "Complementary of \(color)"
}
}
To make it actually work as a library, you have to be explicit:
public struct PaletteGenerator {
public init() {} // Don't forget the initializer!
public func generateComplementary(to color: String) -> String {
return "Complementary of \(color)"
}
}
I mentioned the init because that trips people up constantly. Even if your struct is public, the default memberwise initializer is internal. If you want someone to be able to write let gen = PaletteGenerator() in their own app, you have to write that public init() {} yourself.
Organizing the Folder Structure
SPM is very opinionated about where files go. If you stray from the expected folder structure, the compiler will just stare at you blankly. For a library, your folder hierarchy should look like this:
- Root Folder
Package.swift- Sources
- ColorPalette (This folder name MUST match the target name)
PaletteGenerator.swift
- ColorPalette (This folder name MUST match the target name)
- Tests
- ColorPaletteTests
PaletteGeneratorTests.swift
- ColorPaletteTests
If you name your target ColorPalette in the manifest but name the folder ColorPaletteLogic, SPM won't find your code. It's a strict mapping, so keep them identical.
📋 Practical Task
Build a "StringMetric" Utility Library
Your goal is to create a Swift Package that provides a utility for analyzing strings. This will test your ability to set up a manifest and handle access modifiers correctly.
Requirements:
- Create a package named
StringMetric. - In the
Package.swiftfile, define one library product and one target, both namedStringMetric. - Inside the
Sources/StringMetricfolder, create a public struct calledTextAnalyzer. - Implement a public function inside
TextAnalyzercalledwordCount(in text: String) -> Intthat returns the number of words in a given string. - Ensure
TextAnalyzerhas apublic init()so it can be instantiated from outside the package. - Verify that the folder structure matches the target names exactly.
There are no comments for now.