Imported from thiennc-teso/ios-skills (
skills/swiftui-uikit-interop/SKILL.md). Install upstream withnpx skills add thiennc-teso/ios-skills --skill swiftui-uikit-interop. Copyright stays with the author.
SwiftUI-UIKit Interop
Bridge UIKit and SwiftUI in both directions: wrap UIKit views and controllers for use in SwiftUI, embed SwiftUI views into existing UIKit view hierarchies, and coordinate state updates safely. Targets Swift 6.3 / iOS 26+.
Contents
- Bridging Directions
- Representable Lifecycle and Coordinator
- State Synchronization and Feedback Loops
- Embedding SwiftUI in UIKit
- Route by Task
- Common Mistakes
- Review Checklist
- References
Bridging Directions
- UIKit into SwiftUI:
UIViewRepresentable: Wraps a customUIView(e.g., text views, maps, custom controls).UIViewControllerRepresentable: Wraps aUIViewController(e.g., camera controllers, document pickers, share sheets).
- SwiftUI into UIKit:
UIHostingController: Embeds SwiftUI views into UIKit view controller hierarchies.UIHostingConfiguration: Embeds SwiftUI views directly intoUICollectionViewandUITableViewcells (iOS 16+).
Representable Lifecycle and Coordinator
makeCoordinator() ──> makeUIView(context:) ──> updateUIView(uiView, context:) ──> dismantleUIView(uiView, coordinator:)
makeCoordinator(): Called once to create the delegate/coordinator object.makeUIView(context:): Called once to instantiate the UIKit view and assign delegates.updateUIView(_:context:): Called on every SwiftUI state change affecting the view. Must be idempotent.dismantleUIView(_:coordinator:): Cleanup point when the view leaves the hierarchy. Invalidate timers, unregister observers.
State Synchronization and Feedback Loops
[!WARNING] Prevent infinite update cycles: When UIKit notifies the coordinator of a change (e.g.,
textViewDidChange), the coordinator updates SwiftUI state via@Binding. This triggersupdateUIView. Always check for value equality before applying updates to the UIKit view:if uiView.text != text { uiView.text = text }
Embedding SwiftUI in UIKit
When adding a UIHostingController as a child view controller:
addChild(hostingController)view.addSubview(hostingController.view)- Set Auto Layout constraints
hostingController.didMove(toParent: self)
Use UIHostingConfiguration for modern cell layouts in UICollectionView/UITableView without manual controller management.
Route by Task
- For wrapping
MKMapView,UITextView, and camera capture controllers, read Map, Text, and Camera Wrappers. - For
PHPickerViewController,MFMailComposeViewController,UIActivityViewController, and search controllers, read Picker, Mail, Share, and Search Wrappers. - For
PDFView,QLPreviewController, andMFMessageComposeViewController, read PDF and Message Wrappers. - For embedding SwiftUI in UIKit table/collection cells, navigation transitions, and full hosting migration, read Hosting Migration.
Common Mistakes
- Re-creating heavy UIKit objects inside
updateUIViewinstead of configuring the existing instance. - Omitting equality checks in
updateUIView, triggering continuous render feedback loops. - Adding a
UIHostingController's view without callingaddChildanddidMove(toParent:). - Forgetting to clean up delegates, KVO, or NotificationCenter observers in
dismantleUIView. - Ignoring safe area insets and sizing calculations (
sizeThatFits) in custom representables.
Review Checklist
-
makeCoordinatorused for delegates, target-actions, and data sources -
updateUIViewguards against redundant assignments to avoid feedback loops - Child view controller containment calls (
addChild,didMove) properly paired - Cell layouts in UIKit use
UIHostingConfigurationwhere available - Subscribed observers and display links invalidated in
dismantleUIView - Auto Layout constraints configured with
translatesAutoresizingMaskIntoConstraints = false
