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
102: Publishing a Swift Package
A few years ago, I was working on a suite of three different internal apps for a client. Each app had its own slightly different implementation of a "UserSession" manager—the logic for handling JWT tokens, expiration, and refreshing. Every time the backend team changed the token format, I had to manually hunt down the logic in three different repositories, apply the fix, and hope I didn't miss a line of code in the third app. It was a tedious, error-prone loop that felt completely unnecessary. The moment I extracted that logic into a standalone Swift Package and published it to our private GitHub organization, that friction disappeared. I updated the code once, bumped the version, and the apps just requested the update. That's the power of publishing: you stop repeating yourself across projects.
Tagging Your Code for the Public
Before you can let other developers (or your future self) use your package, you need to move it from a local folder on your Mac to a remote repository. Most of us use GitHub or GitLab, and for Swift Package Manager (SPM), the repository URL is the "address" of your code. But simply pushing your code to main isn't enough for a professional package.
If you just point a project to a branch, any change you push to that branch could potentially break every app using your package. We avoid this by using Git tags. A tag acts as a permanent snapshot of your code at a specific moment. When you run git tag -a 1.0.0 -m "Initial release" and push that tag to the server, you've created a stable anchor. Now, when someone adds your package, they aren't just getting "whatever is on main"—they're getting exactly what was in the 1.0.0 release. I've seen far too many developers skip this step, only to accidentally break a production app because they pushed a "quick fix" to main that changed a public function signature.
Navigating Semantic Versioning
You'll notice I used 1.0.0. This isn't arbitrary; it's called Semantic Versioning (SemVer), and in the Swift ecosystem, it's the law of the land. The three numbers represent Major.Minor.Patch. If you're just fixing a typo in a comment or a bug that doesn't change how the functions are called, you bump the Patch (1.0.1). If you add a new feature—like adding a logToConsole() method to a NetworkLogger package—that's a Minor update (1.1.0). But if you rename a public class or delete a method, you've introduced a "breaking change." That requires a Major bump (2.0.0).
SPM relies on this logic. When you set a dependency to "Up to Next Major Version," Xcode handles the updates for you. It knows it can safely grab 1.1.0 or 1.0.1, but it will never automatically jump to 2.0.0 because it knows your code might stop compiling. It's a safety net that allows you to iterate on your utility libraries without terrifying the people using them.
Making Your Package Discoverable
Once your code is pushed and tagged, the actual "publishing" part is surprisingly simple because SPM is decentralized. There is no "App Store" for packages; the Git repository is the distribution point. To let someone else use your NetworkLogger, you simply provide them with the HTTPS URL of your repository.
When they add this URL in Xcode's "Package Dependencies" section, Xcode looks at the tags on the remote repo, finds the latest version that matches the requested range, and clones that specific snapshot into the project's build folder. I always recommend adding a clear README.md to the root of your package. Since there's no centralized marketplace, your README is your documentation, your marketing, and your user manual all rolled into one. If I can't figure out how to initialize your package within thirty seconds of reading the README, I'm probably going to look for a different library.
📋 Practical Task
Publishing the "CurrencyFormatter" Utility
Now it's your turn to move from local development to a published state. You will create a small utility package that handles currency formatting and make it available for use in a separate project.
- Create the Package: Build a Swift Package named
CurrencyFormatterwith a public functionformat(amount: Double, code: String) -> Stringthat returns a formatted currency string (e.g., "$1,200.00"). - Host the Code: Initialize a Git repository locally, create a public repository on GitHub (or a similar service), and push your code to the
mainbranch. - Version the Release: Use the terminal to create a git tag
1.0.0and push that tag to your remote repository. - Verify Integration: Create a brand new, empty Xcode Project. Add your
CurrencyFormatterpackage using its remote URL, specifying the version1.0.0. Call the formatting function within the project to ensure the dependency is resolving correctly.
There are no comments for now.