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
 
 