Migrating from SwiftUI to UIKit App Lifecycle
Starting with SwiftUI for your iOS app but need to switch to UIKit’s lifecycle? Whether you’re facing SwiftUI limitations or need more control over your app’s behavior, this guide walks you through the migration process while comparing both frameworks’ strengths and weaknesses.
Understanding the Frameworks
SwiftUI, introduced in 2019, brings a declarative approach to iOS development with modern syntax that simplifies UI creation. Its automatic handling of interface updates and state management makes it attractive for new projects.
UIKit, the foundation of iOS development since the beginning, offers an imperative approach with fine-grained control over every aspect of your app’s behavior. Its maturity brings extensive documentation, third-party support, and battle-tested solutions.
Migration Steps: From SwiftUI to UIKit
Step 1: Remove SwiftUI App Protocol Conformance
First, remove the @main
attribute and App
protocol conformance from your SwiftUI app structure:
Before:
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
After:
import SwiftUI
struct MyApp {
// Remove or delete this file entirely
}
Step 2: Create AppDelegate
Create a new AppDelegate.swift
file:
import UIKit
@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Configure app-wide settings here
return true
}
// MARK: UISceneSession Lifecycle
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
return UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
}
}
Step 3: Create SceneDelegate
Create a SceneDelegate.swift
file to manage your app’s UI lifecycle:
import UIKit
import SwiftUI
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
// Create the SwiftUI view that provides the window contents
let contentView = ContentView()
// Use a UIHostingController as window root view controller
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Handle scene disconnection
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Handle scene activation
}
func sceneWillResignActive(_ scene: UIScene) {
// Handle scene deactivation
}
}
Step 4: Update Info.plist
Add scene configuration to your Info.plist
. Right-click on Info.plist and select “Open As > Source Code”, then add:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Step 5: Build and Test
Build and run your app to ensure the migration is successful. Your SwiftUI views will now run within the UIKit lifecycle, giving you access to traditional app and scene delegate methods.
Framework Comparison
SwiftUI Advantages
- Modern Syntax: Declarative approach reduces boilerplate code
- Automatic Updates: UI automatically reflects state changes
- Built-in Features: Native support for dark mode, dynamic type, and accessibility
- Cross-Platform: Share code between iOS, macOS, watchOS, and tvOS
- Preview Canvas: Live preview accelerates development
SwiftUI Limitations
- Framework Maturity: Fewer resources and third-party libraries
- Limited Customization: Some UI customizations require UIKit fallbacks
- iOS Version Requirements: Requires iOS 13+ (iOS 14+ for many features)
- Performance: Can be slower for complex, dynamic lists
- Debugging: Less straightforward than UIKit
UIKit Strengths
- Maturity: Extensive documentation and community knowledge
- Control: Fine-grained control over every UI aspect
- Third-Party Ecosystem: Vast library of components and tools
- Performance: Optimized for all scenarios
- Compatibility: Supports all iOS versions
UIKit Challenges
- Verbose Syntax: More code required for simple tasks
- Manual Updates: Must manually sync UI with data changes
- Learning Curve: Steeper initial learning curve
- Maintenance: More code to maintain
Hybrid Approach Benefits
Migrating to UIKit lifecycle while keeping SwiftUI views offers unique advantages:
- Gradual Migration: Transition UIKit components incrementally
- Legacy Integration: Easier integration with existing UIKit code
- Full Control: Access to app lifecycle events
- Flexibility: Mix and match frameworks as needed
Common Use Cases for Migration
- Deep Linking: Complex URL handling requiring app delegate methods
- Background Tasks: Sophisticated background processing
- Push Notifications: Custom notification handling
- Third-Party SDKs: Integration with SDKs expecting UIKit lifecycle
- Legacy Code: Gradual migration of existing UIKit apps
Best Practices
- Keep It Simple: Only migrate if you need UIKit-specific features
- Document Changes: Clearly document why migration was necessary
- Test Thoroughly: Ensure all SwiftUI features work correctly
- Consider Alternatives: Evaluate if SwiftUI limitations can be worked around
- Plan for the Future: SwiftUI is Apple’s future direction
Conclusion
Migrating from SwiftUI to UIKit app lifecycle provides access to traditional iOS app architecture while maintaining SwiftUI’s modern UI capabilities. This hybrid approach offers the best of both worlds: UIKit’s maturity and control with SwiftUI’s declarative syntax.
Choose this path when you need specific UIKit lifecycle features or are integrating with legacy code. However, if starting fresh without specific UIKit requirements, consider staying with pure SwiftUI to align with Apple’s forward direction.
The iOS development landscape continues to evolve, and understanding both frameworks positions you to make informed architectural decisions for your apps’ unique requirements.