Enhancing iOS UI Previews: Swift UI Packages & Kotlin Multiplatform Mobile.
Intro:
Remember our adventure in Going Modular β The Kotlin Multiplatform Way? Well, this is a continuation of that. (Sort of π). I say sort of because this article focuses on the Swift side of things. We will explore how creating UI components in a separate Swift package can significantly improve the development experience when working on the iOS App.
This is a blog of an ongoing series on my journey with Kotlin Multiplatform.
- Going Modular β The Kotlin Multiplatform Way
- KMM Preferences Datastore
- KMP Environment Variables (Part 1)
- Intercepting Ktor Network Responses in Kotlin Multiplatform
- Navigating the Waters of Kotlin Multiplatform: Exploring Navigation Solutions
- Enhancing iOS UI Previews: Swift UI Packages & Kotlin Multiplatform Mobile. β You are here. π
If you are interested in the code, here is the Pull Request. Letβs get this party started.
The KMM Preview Headache π€
While working with KMM on iOS, one of my biggest headaches is that Previews donβt load in XCode. I donβt know why XCode does not play nice with KMM. It either takes too long to load or fails. The only way to see what I am creating is by running the application. This is frustrating as it slows development time.
Enter Separate Swift Packages π¦
By relocating our UI components into a separate Swift package, weβre not just organizing our code, but weβre also unlocking a host of benefits that can significantly enhance our development experience:
- Functional SwiftUI Previews
- By isolating views from the KMM framework, providing real-time UI development feedback, and improving the developer experience, we can load views faster.
2. Improved Reusability
- Components can be easily reused across the application, leading to a more modular architecture and reducing code duplication.
3. Streamlined Testing
- With this in place, Snapshot testing becomes a viable option for ensuring UI consistency. Coming soon. π€
4. Optimized Project Structure
- A clear structure makes it easy to maintain and update our components.
App structure:
I ended up creating two packages:
- SwiftUIComponents: This package contains common views used in various screens. Colors, Buttons, CardView, PosterImage etc
- TVManiacUI: This Package includes Screen UI Views. eg. CastList, TrailerList, ShowInfoView. etc
Below is what the structure of the App looks like.
TvManiac/
βββ shared/
β βββ ... (Kotlin shared code)
βββ androidApp/
β βββ ... (Android-specific code)
βββ iosApp/
β βββ iosApp.xcodeproj
β βββ iosApp/
β β βββ ... (iOS app-specific code)
β βββ Modules/
β βββ SwiftUIComponents/
β β βββ Package.swift
β β βββ Sources/
β β βββ SwiftUIComponents/
β β βββComponents/
β β βββ BottomNavigation.swift
β β βββ HeaderContentView.swift
β β βββ ...
β β βββ CollapsibleView.swift
β β βββ TrailerItemView.swift
β β βββ ...
β βββ TVManiacUI/
β βββ Package.swift
β βββ Sources/
β βββ TVManiacUI/
β βββModels/
β βββ SwiftCast.swift
β βββ SwiftTrailer.swift
β βββ CastListView.swift
β βββ ShowInfoView.swift
β βββ ProviderListView.swift
β βββ ...
Implementing the Approach
Integrating a package in XCode is a straightforward process. These are the steps I followed:
1. Package Creation and Configuration
- Create a new Swift package (`SwiftUIComponents`) using your preferred method (Xcode UI or Swift Package Manager CLI).
- Configure the `Package.swift` file to specify the iOS deployment target and any necessary dependencies.
2. Component Migration
- Migrate SwiftUI views and components from your KMP iOS project to the new package.
- Refactor as needed to ensure components are self-contained and donβt rely on KMM-specific code.
3. Dependency Management
- If your UI components require a specific package, use Swift Package Managerβs dependency declaration to manage package relationships. In my case, I added `SDWebImageSwiftUI` and `YouTubePlayerKit` and removed them from the main app.
4. Final Step
- In your main iOS project, go to File > Add Packages.
- Select `SwiftUIComponents` package.
- (Optional) Create a new scheme and select the framework, in this case, `SwiftUIComponents.` This allows us to switch between the Main app and the UI package.
With that in place, hereβs an example of how the project structure looks after implementing this approach. π₯³
Conclusion
I highly recommend trying this approach. Itβs made my life easier, my code cleaner, and the general structure better. Next, we might add snapshot tests, a CI/CD job, and more cleanup.
Until we meet again, folks. Happy coding! βοΈ