Add ContentView, Improve size changing in FreeForm Previews, add shortcut for constraints in center#14
Conversation
… of different sizes, such as a small device or a small device with a presented keyboard Add support for snapping to views representing root UIViewControllers of different sizes, such as a small device or a small device with a presented keyboard Rename UIViewControllerFreeFormPreview to FreeForm Add support for previewing a view in freeform for view controllers Delete support for iPhone 11 frame, keep only the small device frame Keep only FreeForm for UIView and UIViewController Remove context argument from view and viewcontroller init methods of FreeForm Previews
Fix containerView is not available
|
Warning Rate limit exceeded@Adobels has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 23 minutes and 38 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (9)
WalkthroughAdds a new center anchor in constraints, introduces IBContainerView for embedding child view controllers, renames stack factory functions, updates UIStackView initializer to include frame, adjusts UIViewDSL protocol and extensions, adds a result-builder buildArray, re-exports UIKit/SwiftUI, adds a test, and removes multiple SwiftUI preview bridge types. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor UserApp
participant Container as IBContainerView
participant Responder as Responder Chain
participant ParentVC as Nearest UIViewController
participant ChildVC as UIViewController (to embed)
rect rgba(230,245,255,0.6)
note over Container: Setup phase
UserApp->>Container: ibEmbed(_ vc) or ibEmbed(maker:)
Container-->>Container: store controllerCreator
end
rect rgba(235,255,235,0.6)
note over Container: Lifecycle
Container->>Container: didMoveToWindow()
alt controllerCreator exists
Container->>Container: make ChildVC
Container->>Responder: nearestViewController()
Responder-->>Container: ParentVC
Container->>ParentVC: ibEmbed(ChildVC, in: Container)
note right of ParentVC: Adds as child, attaches view to Container
else
note over Container: No-op
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (10)
Sources/UIViewKit/UIViewDSL/UIViewDSL+ResultBuilders.swift (1)
46-80: Consider mirroring buildArray for constraints builderLoops that emit constraints will currently fail to compile without
buildArrayonIBLayoutConstraintBuilder. Add a symmetric helper.Apply:
public enum IBLayoutConstraintBuilder { public static func buildBlock(_ components: [NSLayoutConstraint]...) -> [NSLayoutConstraint] { components.flatMap { $0 } } + public static func buildArray(_ components: [[NSLayoutConstraint]]) -> [NSLayoutConstraint] { + components.flatMap { $0 } + } + public static func buildBlock(_ components: NSLayoutConstraint...) -> [NSLayoutConstraint] { components }Sources/UIViewKit/IBConstraints/IBConstraints.swift (2)
42-45: Center anchor implementation looks good; consider offset support laterThe zero-offset
.centeris useful. If you foresee non-zero offsets, a follow-up API (e.g.,.centerX(_:)+.centerY(_:)combo, or a new.centerWithOffset(CGPoint)) could reduce boilerplate.
72-72: Add convenience static for .center to match other anchorsParity with existing
static varhelpers improves ergonomics.Apply:
case trailing(CGFloat) case all static public var left: ViewAnchor { return .left(.zero) } static public var right: ViewAnchor { return .right(.zero) } static public var top: ViewAnchor { return .top(.zero) } static public var bottom: ViewAnchor { return .bottom(.zero) } + static public var center: ViewAnchor { return .center } static public var centerX: ViewAnchor { return .centerX(.zero) } static public var centerY: ViewAnchor { return .centerY(.zero) } static public var leading: ViewAnchor { return .leading(.zero) } static public var trailing: ViewAnchor { return .trailing(.zero) }Sources/UIViewKit/Views/IBVerticalStack.swift (1)
10-13: Use the existing convenience init to reduce duplicationLeverage
UIStackView(axis:spacing:alignment:distribution:)for brevity and consistency withIBHorizontalStack.Apply:
-public func IBVerticalStack(spacing: CGFloat? = nil, alignment: UIStackView.Alignment? = nil, distribution: UIStackView.Distribution? = nil) -> UIStackView { - let stackView = UIStackView() - stackView.axis = .vertical - if let alignment { - stackView.alignment = alignment - } - if let distribution { - stackView.distribution = distribution - } - if let spacing { - stackView.spacing = spacing - } - return stackView -} +public func IBVerticalStack(spacing: CGFloat? = nil, + alignment: UIStackView.Alignment? = nil, + distribution: UIStackView.Distribution? = nil) -> UIStackView { + UIStackView(axis: .vertical, spacing: spacing, alignment: alignment, distribution: distribution) +}Sources/UIViewKit/Views/IBHorizontalStack.swift (1)
10-13: Mirror the convenience init usage here as wellKeeps both factories uniform and concise.
Apply:
-public func IBHorizontalStack(spacing: CGFloat? = nil, alignment: UIStackView.Alignment? = nil, distribution: UIStackView.Distribution? = nil) -> UIStackView { - let stackView = UIStackView() - stackView.axis = .horizontal - if let alignment { - stackView.alignment = alignment - } - if let distribution { - stackView.distribution = distribution - } - if let spacing { - stackView.spacing = spacing - } - return stackView -} +public func IBHorizontalStack(spacing: CGFloat? = nil, + alignment: UIStackView.Alignment? = nil, + distribution: UIStackView.Distribution? = nil) -> UIStackView { + UIStackView(axis: .horizontal, spacing: spacing, alignment: alignment, distribution: distribution) +}Sources/UIViewKit/IBPreviews/IBRepresentables.swift (1)
42-44: Nit: remove stray space before init parameter label for consistencySmall formatting consistency fix.
- public init (_ viewMaker: @escaping () -> UIView) { + public init(_ viewMaker: @escaping () -> UIView) {Sources/UIViewKit/Views/IBContainerView.swift (1)
40-43: Pin child view to container with constraints so it resizes with IBContainerViewThe current ibEmbed implementation in UIViewController+Extensions uses frames; without autoresizing masks or constraints, the child view won’t follow container size changes.
Add edge constraints after embedding:
private func embed(_ viewControllerToEmbed: UIViewController) { guard let parent = nearestViewController() else { return } parent.ibEmbed(viewControllerToEmbed, self) + // Ensure child view resizes with the container + let childView = viewControllerToEmbed.view! + childView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + childView.topAnchor.constraint(equalTo: topAnchor), + childView.leadingAnchor.constraint(equalTo: leadingAnchor), + childView.trailingAnchor.constraint(equalTo: trailingAnchor), + childView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) }If you prefer, consider updating UIViewController.ibEmbed to set constraints instead of frames for a single-source fix.
Sources/UIViewKit/IBPreviews/IBPreviewSizeThatFits.swift (2)
40-49: Use the provided closure parameter instead of capturing outerviewto improve clarityMinor readability fix and avoids accidental capture if this code gets moved.
- if let viewMaker = viewMaker { - let viewToPreview = viewMaker() - view.ibSubviews { - viewToPreview.ibAttributes { - $0.ibConstraints(to: view, guide: .view, anchors: .center) - } - } + if let viewMaker = viewMaker { + let viewToPreview = viewMaker() + view.ibSubviews { superview in + viewToPreview.ibAttributes { + $0.ibConstraints(to: superview, guide: .view, anchors: .center) + } + } return } else if let viewControllerMaker = viewControllerMaker {
60-63: Provide a diagnostic message instead of a bare fatalErrorCrashing is fine for a preview-only type, but a message helps during misuse.
- } else { - fatalError() - } + } else { + preconditionFailure("IBPreviewSizeThatFits requires either a view or a viewController maker.") + }Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift (1)
115-121: Initial width/height constants rely onview.frameinside loadViewInside loadView the root view’s frame can be .zero; prefer deferring initial sizing to viewDidLayoutSubviews or using safe area/bounds to avoid starting at size 0x0 on first layout.
Example approach:
- Initialize with a reasonable default (e.g., 375x667).
- Or set constants in viewDidLayoutSubviews when
view.bounds.sizeis known.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
Sources/UIViewKit/IBConstraints/IBConstraints.swift(2 hunks)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormView.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormViewController.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenView.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenViewController.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreview+SizeThatFitsView.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreview.swift(0 hunks)Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift(1 hunks)Sources/UIViewKit/IBPreviews/IBPreviewSizeThatFits.swift(1 hunks)Sources/UIViewKit/IBPreviews/IBRepresentables.swift(1 hunks)Sources/UIViewKit/UIViewDSL/UIViewDSL+ResultBuilders.swift(1 hunks)Sources/UIViewKit/Views/IBContainerView.swift(1 hunks)Sources/UIViewKit/Views/IBHorizontalStack.swift(1 hunks)Sources/UIViewKit/Views/IBVerticalStack.swift(1 hunks)Tests/UIViewKitTests/IBSubviewsTests.swift(1 hunks)
💤 Files with no reviewable changes (6)
- Sources/UIViewKit/IBPreviews/IBPreview+FreeFormView.swift
- Sources/UIViewKit/IBPreviews/IBPreview+FreeFormViewController.swift
- Sources/UIViewKit/IBPreviews/IBPreview.swift
- Sources/UIViewKit/IBPreviews/IBPreview+SizeThatFitsView.swift
- Sources/UIViewKit/IBPreviews/IBPreview+FullScreenView.swift
- Sources/UIViewKit/IBPreviews/IBPreview+FullScreenViewController.swift
🧰 Additional context used
🧬 Code graph analysis (9)
Tests/UIViewKitTests/IBSubviewsTests.swift (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (2)
ibSubviews(13-17)ibSubviews(19-26)
Sources/UIViewKit/IBConstraints/IBConstraints.swift (1)
Sources/UIViewKit/UIKitExtensions/UIView+Extensions.swift (2)
ibConstraints(10-15)ibConstraints(12-14)
Sources/UIViewKit/UIViewDSL/UIViewDSL+ResultBuilders.swift (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (3)
Self(10-42)ibSubviews(13-17)callAsFunction(28-32)
Sources/UIViewKit/IBPreviews/IBPreviewSizeThatFits.swift (5)
Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift (2)
loadView(40-63)loadView(78-133)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBAttributes.swift (1)
ibAttributes(13-18)Sources/UIViewKit/UIKitExtensions/UIView+Extensions.swift (1)
ibConstraints(12-14)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenView.swift (2)
iOS(11-41)iOS(13-40)Sources/UIViewKit/IBPreviews/IBPreview+SizeThatFitsView.swift (2)
iOS(10-45)iOS(12-44)
Sources/UIViewKit/IBPreviews/IBRepresentables.swift (4)
Sources/UIViewKit/IBPreviews/IBPreview+FreeFormViewController.swift (3)
iOS(11-34)iOS(13-33)updateUIViewController(32-32)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenViewController.swift (3)
iOS(12-30)iOS(10-31)updateUIViewController(29-29)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormView.swift (2)
iOS(11-44)iOS(13-43)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenView.swift (2)
iOS(11-41)iOS(13-40)
Sources/UIViewKit/Views/IBContainerView.swift (1)
Sources/UIViewKit/UIKitExtensions/UIViewController+Extensions.swift (3)
ibEmbed(22-27)ibSetView(10-45)ibUnembed(39-44)
Sources/UIViewKit/Views/IBHorizontalStack.swift (2)
Sources/UIViewKit/Views/HorizontalStack.swift (1)
HorizontalStack(10-23)Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift (1)
axis(10-25)
Sources/UIViewKit/Views/IBVerticalStack.swift (3)
Sources/UIViewKit/Views/HorizontalStack.swift (1)
HorizontalStack(10-23)Sources/UIViewKit/Views/VerticalStack.swift (1)
VerticalStack(10-23)Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift (1)
axis(10-25)
Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift (5)
Sources/UIViewKit/IBPreviews/IBPreviewSizeThatFits.swift (1)
loadView(40-63)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBAttributes.swift (1)
ibAttributes(13-18)Sources/UIViewKit/UIKitExtensions/UIView+Extensions.swift (1)
ibConstraints(12-14)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormView.swift (2)
iOS(11-44)iOS(13-43)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormViewController.swift (4)
iOS(11-34)iOS(13-33)childViewController(36-70)childViewController(38-69)
🔇 Additional comments (4)
Sources/UIViewKit/UIViewDSL/UIViewDSL+ResultBuilders.swift (1)
41-43: buildArray for subview builder: LGTMThis correctly unlocks for-loop support in
@IBSubviewsBuilderand flattens nested arrays.Tests/UIViewKitTests/IBSubviewsTests.swift (1)
77-84: Loop-based subview creation test: LGTMThis verifies the result-builder
buildArraypath and guards against regressions.Sources/UIViewKit/Views/IBHorizontalStack.swift (1)
10-13: All oldHorizontalStack/VerticalStackcall sites removed
No occurrences of the legacy factory functions were found, so no shims are required.Sources/UIViewKit/IBPreviews/IBRepresentables.swift (1)
8-12: Remove unnecessary SwiftUI availability guards
The package’sPackage.swiftspecifies a minimum deployment target of iOS 13, so importing SwiftUI and usingUIViewControllerRepresentable/UIViewRepresentableis always valid—no#if canImport(SwiftUI)or@available(iOS 13.0, *)needed.Likely an incorrect or invalid review comment.
| public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { | ||
| super.touchesBegan(touches, with: event) | ||
| guard let touch = touches.first else { return } | ||
| let loc = touch.location(in: view) | ||
| heightConstraint.constant = loc.y | ||
| widthConstraint.constant = loc.x | ||
| } |
There was a problem hiding this comment.
Bug: calling super.touchesBegan inside touchesMoved
This breaks UIKit’s touch handling chain and can cause unexpected behaviors.
- public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
- super.touchesBegan(touches, with: event)
+ public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
+ super.touchesMoved(touches, with: event)
guard let touch = touches.first else { return }
let loc = touch.location(in: view)
heightConstraint.constant = loc.y
widthConstraint.constant = loc.x
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { | |
| super.touchesBegan(touches, with: event) | |
| guard let touch = touches.first else { return } | |
| let loc = touch.location(in: view) | |
| heightConstraint.constant = loc.y | |
| widthConstraint.constant = loc.x | |
| } | |
| public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { | |
| super.touchesMoved(touches, with: event) | |
| guard let touch = touches.first else { return } | |
| let loc = touch.location(in: view) | |
| heightConstraint.constant = loc.y | |
| widthConstraint.constant = loc.x | |
| } |
🤖 Prompt for AI Agents
In Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift around lines 148 to 154,
the touchesMoved override incorrectly calls super.touchesBegan which breaks
UIKit’s touch chain; change the call to super.touchesMoved(touches, with: event)
and keep the existing touch handling (guard, location, constraint updates) so
the superclass receives the correct moved event.
| func tapGesture() -> UIGestureRecognizer { | ||
| UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:))) | ||
| } | ||
|
|
||
| @objc | ||
| func didTap(gesture: UIGestureRecognizer) { | ||
| guard let tapGesture = gesture as? UITapGestureRecognizer else { return } | ||
| for viewToSnap in self.viewToSnap { | ||
| let loc = tapGesture.location(in: viewToSnap) | ||
| if viewToSnap.frame.contains(loc) { | ||
| controller.widthConstraint.constant = viewToSnap.frame.width | ||
| controller.heightConstraint.constant = viewToSnap.frame.height | ||
| return | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: hit-testing uses frame.contains with a point in the view’s own coordinates
You’re calling location(in: viewToSnap), which returns a point in the view’s local coordinates; compare against bounds, not frame. Also, consider not canceling touches so the content remains interactive.
- func tapGesture() -> UIGestureRecognizer {
- UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:)))
- }
+ func tapGesture() -> UIGestureRecognizer {
+ let g = UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:)))
+ g.cancelsTouchesInView = false
+ return g
+ }
@@
- let loc = tapGesture.location(in: viewToSnap)
- if viewToSnap.frame.contains(loc) {
+ let loc = tapGesture.location(in: viewToSnap)
+ if viewToSnap.bounds.contains(loc) {
controller.widthConstraint.constant = viewToSnap.frame.width
controller.heightConstraint.constant = viewToSnap.frame.height
return
}🤖 Prompt for AI Agents
In Sources/UIViewKit/IBPreviews/IBPreviewFreeForm.swift around lines 169–184,
the tap handler does hit-testing with viewToSnap.frame.contains(loc) using a
point returned by location(in: viewToSnap) (which is in the view’s local
coordinates); change the hit test to use viewToSnap.bounds.contains(loc)
instead, and modify tapGesture() to create a UITapGestureRecognizer instance,
set its target/action, and set recognizer.cancelsTouchesInView = false so the
gesture doesn’t block other touch handling, then return that recognizer.
…iftUI Views in IBPreviewFreeForm
877179f to
6823123
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift (1)
12-24: Optional: mark initializer @mainactor (and consider a sugar overload).UI work should be confined to the main thread; annotating helps concurrency correctness. Also, keeping an axis-first overload can aid readability.
Apply within this range:
- public convenience init(frame: CGRect = .zero, axis: NSLayoutConstraint.Axis, spacing: CGFloat? = nil, alignment: UIStackView.Alignment? = nil, distribution: UIStackView.Distribution? = nil) { + @MainActor public convenience init(frame: CGRect = .zero, axis: NSLayoutConstraint.Axis, spacing: CGFloat? = nil, alignment: UIStackView.Alignment? = nil, distribution: UIStackView.Distribution? = nil) {Additionally (outside this range), consider adding:
@MainActor public convenience init(axis: NSLayoutConstraint.Axis, spacing: CGFloat? = nil, alignment: UIStackView.Alignment? = nil, distribution: UIStackView.Distribution? = nil) { self.init(frame: .zero, axis: axis, spacing: spacing, alignment: alignment, distribution: distribution) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift (2)
Tests/UIViewKitTests/UIStackViewExtensionsTests.swift (4)
testVertical(11-46)testVerticalWithArguments(23-29)testHorizontalWithArguments(39-45)testVertical(15-21)Tests/UIViewKitTests/StackViewTests.swift (2)
testVerticalStackView(24-31)testHorizontalStackView(15-22)
🔇 Additional comments (1)
Sources/UIViewKit/UIKitExtensions/UIStackView+Extensions.swift (1)
12-24: LGTM: frame-aware initializer is correct and source-compatible.Uses self.init(frame:) appropriately; defaults preserved when optionals are nil (axis-first calls only appear in tests).
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift (2)
10-10: Main-actor isolate these UIView helpersThese APIs touch UI-owned references; isolating the extension to the main actor prevents accidental background usage and aligns with the rest of the DSL.
-import UIKit - -extension UIViewDSL { +import UIKit + +@MainActor +extension UIViewDSL {
37-46: Tighten Owner to AnyObject for ReferenceWritableKeyPathReferenceWritableKeyPath requires a class root; constraining Owner clarifies intent and avoids surprising inference.
- public func ibOutlet<Owner: Any>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, Self>) -> Self { + public func ibOutlet<Owner: AnyObject>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, Self>) -> Self { owner?[keyPath: property] = self return self } @discardableResult - public func ibOutlet<Owner: Any>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, Self?>) -> Self { + public func ibOutlet<Owner: AnyObject>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, Self?>) -> Self { owner?[keyPath: property] = self return self }Sources/UIViewKit/UIViewDSL/UIViewDSL.swift (1)
10-10: Consider annotating the protocol itself with @mainactorOptional, but it propagates UI-thread guarantees to default impls in protocol extensions.
-public protocol UIViewDSL where Self: UIView { } +@MainActor +public protocol UIViewDSL where Self: UIView { }Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (2)
20-23: Avoid force casts in content wrappersUse a safe cast + assertion to prevent crashes if misuse ever slips in.
- let contentWrapper: (UIView) -> [UIView] = { arg1 in - content(arg1 as! Self) // swiftlint:disable:this force_cast - } + let contentWrapper: (UIView) -> [UIView] = { owner in + guard let typedOwner = owner as? Self else { + assertionFailure("ibSubviews owner type mismatch: expected \(Self.self), got \(type(of: owner))") + return [] + } + return content(typedOwner) + }- let contentWrapper: (UIView) -> [UIView] = { arg1 in - content(arg1 as! Self) // swiftlint:disable:this force_cast - } + let contentWrapper: (UIView) -> [UIView] = { owner in + guard let typedOwner = owner as? Self else { + assertionFailure("ibSubviews owner type mismatch: expected \(Self.self), got \(type(of: owner))") + return [] + } + return content(typedOwner) + }Also applies to: 35-38
28-32: DRY: have callAsFunction forward to ibSubviewsKeeps behavior identical and reduces duplication.
@discardableResult public func callAsFunction(@IBSubviewsBuilder _ content: () -> [UIView]) -> Self { - UIViewDSLEngine.shared.addSubviews(content, to: self) - return self + return ibSubviews(content) }@discardableResult public func callAsFunction(@IBSubviewsBuilder _ content: (Self) -> [UIView]) -> Self { - let contentWrapper: (UIView) -> [UIView] = { arg1 in - content(arg1 as! Self) // swiftlint:disable:this force_cast - } - UIViewDSLEngine.shared.addSubviews(contentWrapper, to: self) - return self + return ibSubviews(content) }Also applies to: 14-17
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBAttributes.swift(1 hunks)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift(1 hunks)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift(1 hunks)Sources/UIViewKit/UIViewDSL/UIViewDSL.swift(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Sources/UIViewKit/UIViewDSL/UIViewDSL+IBAttributes.swift
🧰 Additional context used
🧬 Code graph analysis (3)
Sources/UIViewKit/UIViewDSL/UIViewDSL.swift (2)
Sources/UIViewKit/UIViewDSL/AutoresizingStrategy/UIViewDSL+EngineConstraintsProtocol.swift (1)
addConstraints(10-19)Sources/UIViewKit/UIViewDSL/UIViewDSL+Engine.swift (2)
shared(10-66)addConstraints(37-42)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+NSLayoutConstraint.swift (1)
ibOutlet(29-33)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+Engine.swift (3)
shared(10-66)addSubviews(68-85)addSubviews(70-84)
🔇 Additional comments (2)
Sources/UIViewKit/UIViewDSL/UIViewDSL.swift (1)
10-12: Verified: only UIView conforms to UIViewDSL
No other explicit conformances found.Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (1)
10-11: Extension isolation and header change look goodUnconstraining the extension is fine given the protocol’s Self: UIView bound, and @mainactor keeps UI ops safe.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
Sources/UIViewKit/UIViewKit.swift (2)
8-9: Guard re-exports and avoid hard dependency on platform-only frameworks.Using @_exported is fine here, but it’s an underscored attribute and you’re re-exporting heavy, platform-scoped modules. Wrap with canImport to keep the package flexible across platforms/toolchains.
Apply:
-@_exported import UIKit -@_exported import SwiftUI +#if canImport(UIKit) +@_exported import UIKit +#endif +#if canImport(SwiftUI) +@_exported import SwiftUI +#endif
1-7: Optional: replace the file banner with a concise module doc comment.Keep headers minimal and explain why this file exists.
-// -// UIViewKit.swift -// UIViewKit -// -// Created by blz on 03/09/2025. -// +/// Re-exports UIKit and SwiftUI so consumers can `import UIViewKit` only. +/// Note: These transitive imports are part of the public API surface.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Sources/UIViewKit/UIViewKit.swift(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Sources/UIViewKit/UIViewKit.swift (6)
Sources/UIViewKit/IBPreviews/IBPreview+FullScreenView.swift (2)
iOS(11-41)iOS(13-40)Sources/UIViewKit/UIViewDSL/UIViewDSL.swift (2)
extension UIView: UIViewDSL { }(12-12)public protocol UIViewDSL { }(10-10)Sources/UIViewKit/IBPreviews/IBPreview+FullScreenViewController.swift (2)
iOS(10-31)iOS(12-30)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormViewController.swift (1)
iOS(11-34)Sources/UIViewKit/IBPreviews/IBPreview+FreeFormView.swift (2)
iOS(13-43)iOS(11-44)Sources/UIViewKit/UIKitExtensions/UIViewController+Extensions.swift (1)
ibSetView(10-45)
🔇 Additional comments (1)
Sources/UIViewKit/UIViewKit.swift (1)
8-9: Confirmed SwiftUI compatibility and API surface
Package.swift specifies iOS 13+ (lines 8–10), matching SwiftUI’s availability. Note that@_exported import UIKit/SwiftUIexpands your public API—removing these later is a semver-breaking change.
Any is working well when owner is AnyObject but when owner is a struct then async calls to struct variables makes the Any unusable
Add explicit @mainactor to UIViewDSL, though it seems unnecessary since the protocol’s Self constraint on UIView is already @MainActor-protected by UIView
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift (1)
10-10: Confirm unconstrained extension; consider adding @mainactor for consistency with other UIViewDSL APIsDropping
where Self: UIViewbroadens availability. If intentional, fine—but to keep UI APIs consistently main-thread-only (as in IBAttributes/IBSubviews), consider annotating this extension with@MainActor.-extension UIViewDSL { +@MainActor +extension UIViewDSL {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift (4)
Sources/UIViewKit/UIViewDSL/UIViewDSL+NSLayoutConstraint.swift (4)
ibOutlet(29-33)ibOutlet(35-39)ibOutlet(41-45)ibOutlet(47-51)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBAttributes.swift (1)
Self(10-19)Sources/UIViewKit/UIViewDSL/UIViewDSL+IBSubviews.swift (1)
Self(10-42)Sources/UIViewKit/UIViewDSL/UIViewDSL.swift (2)
extension UIView: UIViewDSL { }(12-12)public protocol UIViewDSL { }(10-10)
🔇 Additional comments (2)
Sources/UIViewKit/UIViewDSL/UIViewDSL+IBOutlet.swift (2)
37-40: Good call: constrain Owner to AnyObjectUsing
Owner: AnyObjectmatchesReferenceWritableKeyPath’s class-root requirement and prevents accidental struct owners. Please mirror this in the NSLayoutConstraint overloads for consistency.Suggested follow-up in Sources/UIViewKit/UIViewDSL/UIViewDSL+NSLayoutConstraint.swift:
- public func ibOutlet<Owner: Any>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, NSLayoutConstraint>) -> Self { + public func ibOutlet<Owner: AnyObject>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, NSLayoutConstraint>) -> Self {
43-46: Likewise here: AnyObject is correctSame rationale as above; also mirror the optional variant in the NSLayoutConstraint helpers for symmetry.
Follow-up:
- public func ibOutlet<Owner: Any>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, NSLayoutConstraint?>) -> Self { + public func ibOutlet<Owner: AnyObject>(_ owner: Owner?, _ property: ReferenceWritableKeyPath<Owner, NSLayoutConstraint?>) -> Self {
Project is dedicated to Swift code bases only
Summary by CodeRabbit
New Features
Breaking Changes
Tests