Skip to content

Commit

Permalink
Improve DSL experience when working with UIScrollView and other compl…
Browse files Browse the repository at this point in the history
…ex views (#7)

* transform 'ibSetView' to method which extends UIView to increase readibility

* Various in WIP

* add IBScrollView

* Update files header

* update addSubviews to be Self typed

* Delete IBScrollView and exhence involvesOwnerView to support UILayoutGuide

* comment callAsFunction to help Swift type resolver resolve superviews

* comment ibsubview without superview to help swift type resolver to resolve superviews

* add explicit Void expression block to try improve superview typing in ibAttributes

* Revert code added to improve ibAttributes superview type resolver. Problem is present when using expressions within ibAttributes which use implicit assignation
  • Loading branch information
Adobels authored Oct 30, 2023
1 parent cfe796a commit 6eb6653
Show file tree
Hide file tree
Showing 16 changed files with 211 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// InferredAttributesOwnerStrategy.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// InferredConstraintsStrategy.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSLEngineConstraintsProtocol.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
2 changes: 1 addition & 1 deletion Sources/UIViewKit/UIViewDSL+Attributes.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+Attributes.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
2 changes: 1 addition & 1 deletion Sources/UIViewKit/UIViewDSL+Constraints.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+Constraints.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
22 changes: 18 additions & 4 deletions Sources/UIViewKit/UIViewDSL+Helper.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
//
// File.swift
//
// UIViewDSL+Helper.swift
// UIViewKit
//
// Created by MaxAir on 29/09/2023.
// Created by Blazej SLEBODA on 29/09/2023.
//

import UIKit

enum UIViewDSLHelper {

static func involvesOwnerView(_ owner: UIView, in constraint: NSLayoutConstraint) -> Bool {
(constraint.firstItem as? UIView) == owner || (constraint.secondItem as? UIView) == owner
var ownerView: [UIView] = []

if let layoutGuide = constraint.firstItem as? UILayoutGuide, let owningView = layoutGuide.owningView {
ownerView.append(owningView)
}
if let layoutGuide = constraint.secondItem as? UILayoutGuide, let owningView = layoutGuide.owningView {
ownerView.append(owningView)
}
if let owningView = constraint.firstItem as? UIView {
ownerView.append(owningView)
}
if let owningView = constraint.secondItem as? UIView {
ownerView.append(owningView)
}
return ownerView.contains(where: { $0 == owner } )
}

static func addSubviews(_ subviews: [UIView], to target: UIView) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/UIViewKit/UIViewDSL+IBOutlet.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+IBOutlet.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
6 changes: 1 addition & 5 deletions Sources/UIViewKit/UIViewDSL+ResultBuilders.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+ResultBuilders.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down Expand Up @@ -69,10 +69,6 @@ public enum NSLayoutConstraintBuilder {
public static func buildExpression(_ expression: [NSLayoutConstraint]) -> [NSLayoutConstraint] {
expression
}

public static func buildExpression(_ expression: Void) -> [NSLayoutConstraint] {
[]
}

public static func buildExpression(_ expression: Any?) -> [NSLayoutConstraint] {
[]
Expand Down
16 changes: 11 additions & 5 deletions Sources/UIViewKit/UIViewDSL+Subviews.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+Subviews.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand All @@ -17,8 +17,11 @@ extension UIViewDSL where Self: UIView {
}

@discardableResult
public func ibSubviews(@UIViewBuilder _ content: (UIView) -> [UIView]) -> Self {
UIViewDSLEngine.shared.addSubviews(content, to: self)
public func ibSubviews(@UIViewBuilder _ content: (Self) -> [UIView]) -> Self {
let contentWrapper: (UIView) -> [UIView] = { arg1 in
content(arg1 as! Self)
}
UIViewDSLEngine.shared.addSubviews(contentWrapper, to: self)
return self
}

Expand All @@ -29,8 +32,11 @@ extension UIViewDSL where Self: UIView {
}

@discardableResult
public func callAsFunction(@UIViewBuilder _ content: (UIView) -> [UIView]) -> Self {
UIViewDSLEngine.shared.addSubviews(content, to: self)
public func callAsFunction(@UIViewBuilder _ content: (Self) -> [UIView]) -> Self {
let contentWrapper: (UIView) -> [UIView] = { arg1 in
content(arg1 as! Self)
}
UIViewDSLEngine.shared.addSubviews(contentWrapper, to: self)
return self
}
}
2 changes: 1 addition & 1 deletion Sources/UIViewKit/UIViewDSL+SwiftUIStyleStacks.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// UIViewDSL+SwiftUIStyleStacks.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 29/09/2023.
//
Expand Down
22 changes: 0 additions & 22 deletions Sources/UIViewKit/ViewInCode.swift

This file was deleted.

30 changes: 30 additions & 0 deletions Sources/UIViewKit/Views/IBView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// IBView.swift
// UIViewKit
//
// Created by Blazej SLEBODA on 19/10/2023.
//

import UIKit

open class IBView: UIView {

override public init(frame: CGRect) {
super.init(frame: frame)
createView(frame: frame)
}

required public init?(coder: NSCoder) {
fatalError()
}

open func createView(frame: CGRect) { }
}

extension UILabel {

public convenience init(_ text: String) {
self.init(frame: .zero)
self.text = text
}
}
9 changes: 3 additions & 6 deletions Sources/UIViewKitPreviewsDemo/HelloWordView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ public class HelloWordView: UIView {

#if DEBUG

import SwiftUI

struct HelloWordViewPreviews: PreviewProvider {
static var previews: some View {
ViewPreview(HelloWordView())
}
@available(iOS 17.0, *)
#Preview("Default") {
HelloWordView()
}

#endif
38 changes: 38 additions & 0 deletions Tests/UIViewKitTests/IBAttributesTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// IBAttributesTests.swift
// UIViewKit
//
// Created by Blazej SLEBODA on 26/10/2023.
//

import XCTest
@testable import UIViewKit


@MainActor
class IBAttributesTests: XCTestCase {

func testLabelText() throws {
_ = UIView() {
ViewWithLabel().ibAttributes {
print($0)
$0.font = .italicSystemFont(ofSize: 20)
Optional<String>.none
}
}
}
}

fileprivate class ViewWithLabel: IBView {

var font: UIFont!

override func createView(frame: CGRect) {
super.createView(frame: frame)
self {
UILabel().ibAttributes {
$0.font = font
}
}
}
}
93 changes: 93 additions & 0 deletions Tests/UIViewKitTests/IBConstraintsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,97 @@ import XCTest

@MainActor
class ibConstraintsTests: XCTestCase {

func testViewAll() throws {
let view = UIView()
let subview = UIView()

view {
subview.ibAttributes {
$0.ibConstraints(to: view, guide: .view, anchors: .all)
}
}

XCTAssertEqual(view.constraints.count, 4)
XCTAssertEqual(subview.constraints.count, 0)

let firstConstraint = try XCTUnwrap(view.constraints[0])
let secondConstraint = try XCTUnwrap(view.constraints[1])
let thirdConstraint = try XCTUnwrap(view.constraints[2])
let fourthConstraint = try XCTUnwrap(view.constraints[3])

try {
let firstConstraintFirstItem = try XCTUnwrap(firstConstraint.firstItem as? UIView)
let secondConstraintFirstItem = try XCTUnwrap(secondConstraint.firstItem as? UIView)
let thirdConstraintFirstItem = try XCTUnwrap(thirdConstraint.firstItem as? UIView)
let fourthConstraintFirstItem = try XCTUnwrap(fourthConstraint.firstItem as? UIView)

XCTAssertEqual(firstConstraintFirstItem, subview)
XCTAssertEqual(secondConstraintFirstItem, subview)
XCTAssertEqual(thirdConstraintFirstItem, subview)
XCTAssertEqual(fourthConstraintFirstItem, subview)
}()

try {
let firstConstraintSecondItem = try XCTUnwrap(firstConstraint.secondItem as? UIView)
let secondConstraintSecondItem = try XCTUnwrap(secondConstraint.secondItem as? UIView)
let thirdConstraintSecondItem = try XCTUnwrap(thirdConstraint.secondItem as? UIView)
let fourthConstraintSecondItem = try XCTUnwrap(fourthConstraint.secondItem as? UIView)

XCTAssertEqual(firstConstraintSecondItem, view)
XCTAssertEqual(secondConstraintSecondItem, view)
XCTAssertEqual(thirdConstraintSecondItem, view)
XCTAssertEqual(fourthConstraintSecondItem, view)
}()

try {
let firstConstraintFirstAttribute = try XCTUnwrap(firstConstraint.firstAttribute)
let secondConstraintFirstAttribute = try XCTUnwrap(secondConstraint.firstAttribute)
let thirdConstraintFirstAttribute = try XCTUnwrap(thirdConstraint.firstAttribute)
let fourthConstraintFirstAttribute = try XCTUnwrap(fourthConstraint.firstAttribute)

XCTAssertEqual(firstConstraintFirstAttribute.rawValue, 3)
XCTAssertEqual(secondConstraintFirstAttribute.rawValue, 4)
XCTAssertEqual(thirdConstraintFirstAttribute.rawValue, 1)
XCTAssertEqual(fourthConstraintFirstAttribute.rawValue, 2)
}()

try {
let firstConstraintSecondAttribute = try XCTUnwrap(firstConstraint.secondAttribute)
let secondConstraintSecondAttribute = try XCTUnwrap(secondConstraint.secondAttribute)
let thirdConstraintSecondAttribute = try XCTUnwrap(thirdConstraint.secondAttribute)
let fourthConstraintSecondAttribute = try XCTUnwrap(fourthConstraint.secondAttribute)

XCTAssertEqual(firstConstraintSecondAttribute.rawValue, 3)
XCTAssertEqual(secondConstraintSecondAttribute.rawValue, 4)
XCTAssertEqual(thirdConstraintSecondAttribute.rawValue, 1)
XCTAssertEqual(fourthConstraintSecondAttribute.rawValue, 2)
}()

try {
let firstConstraintSecondAnchor = try XCTUnwrap(firstConstraint.secondAnchor)
let secondConstraintSecondAnchor = try XCTUnwrap(secondConstraint.secondAnchor)
let thirdConstraintSecondAnchor = try XCTUnwrap(thirdConstraint.secondAnchor)
let fourthConstraintSecondAnchor = try XCTUnwrap(fourthConstraint.secondAnchor)

XCTAssertEqual(firstConstraintSecondAnchor, view.topAnchor)
XCTAssertEqual(secondConstraintSecondAnchor, view.bottomAnchor)
XCTAssertEqual(thirdConstraintSecondAnchor, view.leftAnchor)
XCTAssertEqual(fourthConstraintSecondAnchor, view.rightAnchor)
}()

try {
let firstConstraintSecondAnchor = try XCTUnwrap(firstConstraint.secondAnchor)
let secondConstraintSecondAnchor = try XCTUnwrap(secondConstraint.secondAnchor)
let thirdConstraintSecondAnchor = try XCTUnwrap(thirdConstraint.secondAnchor)
let fourthConstraintSecondAnchor = try XCTUnwrap(fourthConstraint.secondAnchor)

XCTAssertEqual(firstConstraintSecondAnchor, view.topAnchor)
XCTAssertEqual(secondConstraintSecondAnchor, view.bottomAnchor)
XCTAssertEqual(thirdConstraintSecondAnchor, view.leftAnchor)
XCTAssertEqual(fourthConstraintSecondAnchor, view.rightAnchor)
}()
}

func testRunDifferentConfigurations() throws {
let view = UIView()
Expand All @@ -28,5 +119,7 @@ class ibConstraintsTests: XCTestCase {
$0.ibConstraints(to: view, guide: .viewSafeArea, anchors: .all)
}
}

XCTAssertEqual(view.constraints.count, 40)
}
}
11 changes: 10 additions & 1 deletion Tests/UIViewKitTests/IBSubviewsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,14 @@ class IBSubviewsTests: XCTestCase {
UILabel()
}
}


func testIBSubviewsWithForEach() throws {
let subviews = [
UILabel(),
UIView()
]
_ = UIStackView() {
subviews.filter { _ = $0; return true }
}
}
}

0 comments on commit 6eb6653

Please sign in to comment.