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.
All rights reserved