Best Practices for Managing Third-Party Libraries in Multi-Module iOS Apps


When building a multi-module iOS app using Swift Package Manager, importing third-party libraries like Firebase directly into multiple feature modules quickly becomes messy. It leads to duplicated configurations, tight coupling, and hard-to-maintain code.
The best practice is to create a wrapper module (e.g. FirebaseModule
) that imports and manages all Firebase logic in one place. Then, other modules like LoginModule
, Data
, etc., only depend on this wrapper — they never import the third-party library directly.
1. Create a Wrapper Module for the SDK (e.g. Firebase)
Modules/FirebaseModule/Package.swift
:
// Only in the wrapper module
.package(url: "<https://github.com/firebase/firebase-ios-sdk.git>", branch: "main"),
.target(
name: "FirebaseModule",
dependencies: [
.product(name: "FirebaseAuth", package: "firebase-ios-sdk"),
.product(name: "FirebaseFirestore", package: "firebase-ios-sdk"),
.product(name: "FirebaseStorage", package: "firebase-ios-sdk")
]
)
2. Wrap SDK Logic and Use Protocols
CommonModule/AuthService.swift
:
public protocol AuthService {
func login(email: String, password: String) -> AnyPublisher<User, Error>
}
FirebaseModule/FirebaseAuthService.swift
:
import FirebaseAuth
import Combine
public class FirebaseAuthService: AuthService {
public init() {}
func login(email: String, password: String) -> AnyPublisher<Void, Error> {
Future { promise in
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
promise(.failure(error))
} else {
promise(.success(()))
}
}
}.eraseToAnyPublisher()
}
}
3. Other Modules Import Only the Wrapper
Modules/Data/Package.swift
:
.package(path: "../FirebaseModule")
.target(
name: "Data",
dependencies: ["FirebaseModule", "CommonModule", ...]
)
In your Swift code:
import FirebaseModule
let authService: AuthService = FirebaseAuthService()
Benefits of This Pattern
Clean separation of concerns
No direct Firebase SDK imports in features
Easier unit testing via protocols
Centralized SDK configuration
Avoids duplication and compile issues
This practice applies to any third-party SDK (Firebase, Alamofire, Realm, GRDB, etc.). Keep them wrapped in a dedicated module, expose interfaces via protocols, and let your app scale cleanly.
Thanks for Reading! ✌️
If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account Pham Trung Huy.
Happy coding 🍻
Subscribe to my newsletter
Read articles from Phạm Trung Huy directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Phạm Trung Huy
Phạm Trung Huy
👋 I am a Mobile Developer based in Vietnam. My passion lies in continuously pushing the boundaries of my skills in this dynamic field.