Skip to content

Commit

Permalink
Enhancements, Refactoring, and Fixes for UIViewDSL Component and Asso…
Browse files Browse the repository at this point in the history
…ciated Logic (#2)

* Try to address .swiftpm build sssues on SwiftPackageIndex

* Fix multiple issues related to `.swiftpm` build and UI components:

- Resolve `.swiftpm` build problems on SwiftPackageIndex.
- Correct VerticalStack's axis for designated initializer.
- Enhance view hierarchy display for all subviews.
- Introduce related tests.

* Set Swift minimum version to 5.7 for DSL language features

* dd Swift and Platform Compatibility Badges to README.md

* Update missing informations in files headers

* Refactor anchor constraints to assert UIView presence

* Add tests

* WIP

* move more logic to UIViewDSLEngine

* organize UIViewDSL logic in separate files, write different strategies for autolayout constraints

* Update UIViewDSLEngineConstraintsProtocol to public

* set UIViewDSL to public

* set public init methods on ConstraintsStrategy

* set InferredConstraintsStrategy as experimental + protocolUIViewDSLEngineConstraintsProtocol improvements

* remove prints from code
  • Loading branch information
Adobels authored Oct 1, 2023
1 parent e594b60 commit 9581a5d
Show file tree
Hide file tree
Showing 32 changed files with 937 additions and 395 deletions.
106 changes: 106 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/UIViewKit-Package.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKit"
BuildableName = "UIViewKit"
BlueprintName = "UIViewKit"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKitPreviewsDemo"
BuildableName = "UIViewKitPreviewsDemo"
BlueprintName = "UIViewKitPreviewsDemo"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKitTests"
BuildableName = "UIViewKitTests"
BlueprintName = "UIViewKitTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKitTests"
BuildableName = "UIViewKitTests"
BlueprintName = "UIViewKitTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKit"
BuildableName = "UIViewKit"
BlueprintName = "UIViewKit"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKit"
BuildableName = "UIViewKit"
BlueprintName = "UIViewKit"
BlueprintIdentifier = "UIViewKitPreviewsDemo"
BuildableName = "UIViewKitPreviewsDemo"
BlueprintName = "UIViewKitPreviewsDemo"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
Expand All @@ -28,18 +28,6 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKitTests"
BuildableName = "UIViewKitTests"
BlueprintName = "UIViewKitTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -61,9 +49,9 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "UIViewKit"
BuildableName = "UIViewKit"
BlueprintName = "UIViewKit"
BlueprintIdentifier = "UIViewKitPreviewsDemo"
BuildableName = "UIViewKitPreviewsDemo"
BlueprintName = "UIViewKitPreviewsDemo"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
Expand Down
12 changes: 9 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.4
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -11,14 +11,20 @@ let package = Package(
products: [
.library(
name: "UIViewKit",
targets: ["UIViewKit"]),
targets: ["UIViewKit"]
),
.library(name: "UIViewKitPreviewsDemo", targets: ["UIViewKitPreviewsDemo"])
],
targets: [
.target(
name: "UIViewKit"
),
.target(
name: "UIViewKitPreviewsDemo",
dependencies: ["UIViewKit"]
),
.testTarget(
name: "UIViewKitTests",
dependencies: ["UIViewKit"]),
dependencies: ["UIViewKit", "UIViewKitPreviewsDemo"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FAdobels%2FUIViewKit%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/Adobels/UIViewKit)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FAdobels%2FUIViewKit%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/Adobels/UIViewKit)

# UIViewKit

UIViewKit is a Swift tool that makes designing and setting up UIKit views as simple as using InterfaceBuilder, but with Swift's strong type checks. It offers a look similar to SwiftUI and has lots of easy methods for attributes, outlets, and constraints. Thanks to the @resultBuilder attribute, the code is quick to write, looks cleaner, and is more pleasing to the eye.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// InferredAttributesOwnerStrategy.swift
//
//
// Created by Blazej SLEBODA on 29/09/2023.
//

import UIKit

public class InferredAttributesOwnerStrategy: UIViewDSLEngineConstraintsProtocol {

// MARK: - Private Properties

private var constraintsToApply: [(UIView, [NSLayoutConstraint])] = []


// MARK: - UIViewDSLEngineConstraintsProtocol Methods

public func addConstraints(for owner: UIView, constraints: [NSLayoutConstraint]) {
guard !constraints.isEmpty else { return }
constraints.forEach {
if !UIViewDSLHelper.involvesOwnerView(owner, in: $0) {
fatalError("Added constraints do not involve the specified owner view. Please ensure that constraints are correctly defined for the owner view.")
}
}
constraintsToApply.append((owner, constraints))
}

public func ibSubviewsWillExecute(on rootView: UIView) {
if !constraintsToApply.isEmpty {
fatalError("Attempted to begin subviews definition while constraintsToApply is not empty. This indicates that there may have been a previous incomplete or erroneous subviews definition process.")
}
}

public func ibSubviewsDidExecute(on rootView: UIView) {
activateAutoLayout()
}

public func ibAttributesDidExecute(on rootView: UIView) {
activateAutoLayout()
}

// MARK: - Initializer Methods

public init() { }

// MARK: - Private Methods

private func activateAutoLayout() {
var allConstraints: [NSLayoutConstraint] = []
constraintsToApply.forEach { owner, constraints in
allConstraints.append(contentsOf: constraints)
owner.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate(allConstraints)
constraintsToApply.removeAll()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// InferredConstraintsStrategy.swift
//
//
// Created by Blazej SLEBODA on 29/09/2023.
//

import UIKit

class InferredConstraintsStrategy: UIViewDSLEngineConstraintsProtocol {


// MARK: - Private Properties

private var constraintsToApply: [(UIView, [NSLayoutConstraint])] = []

// MARK: - UIViewDSLEngineConstraintsProtocol Methods

func ibSubviewsWillExecute(on rootView: UIView) {
if !constraintsToApply.isEmpty {
fatalError("Attempted to begin subviews definition while constraintsToApply is not empty. This indicates that there may have been a previous incomplete or erroneous subviews definition process.")
}
}

func ibSubviewsDidExecute(on rootView: UIView) {
activateAutoLayout()
}

func addConstraints(for owner: UIView, constraints: [NSLayoutConstraint]) {
guard !constraints.isEmpty else { return }
constraints.forEach {
if !UIViewDSLHelper.involvesOwnerView(owner, in: $0) {
fatalError("Added constraints do not involve the specified owner view. Please ensure that constraints are correctly defined for the owner view.")
}
}
constraintsToApply.append((owner, constraints))
}

func ibAttributesDidExecute(on rootView: UIView) {
activateAutoLayout()
}

// MARK: - Initializer Methods

init() { }

// MARK: - Private Methods

private func activateAutoLayout() {
var allConstraints: [NSLayoutConstraint] = []
constraintsToApply.forEach { owner, constraints in
allConstraints.append(contentsOf: constraints)
for constraint in constraints {
if let firstView = constraint.firstItem as? UIView {
if firstView.superview != nil {
firstView.translatesAutoresizingMaskIntoConstraints = false
}
}
if let secondView = constraint.secondItem as? UIView {
if secondView.superview != nil {
secondView.translatesAutoresizingMaskIntoConstraints = false
}
}
}
}
NSLayoutConstraint.activate(allConstraints)
constraintsToApply.removeAll()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// UIViewDSLEngineConstraintsProtocol.swift
//
//
// Created by Blazej SLEBODA on 29/09/2023.
//

import UIKit

public protocol UIViewDSLEngineConstraintsProtocol: AnyObject {
func addConstraints(for owner: UIView, constraints: [NSLayoutConstraint])
func ibSubviewsWillExecute(on rootView: UIView)
func ibSubviewsDidExecute(on rootView: UIView)
func ibAttributesDidExecute(on rootView: UIView)
}
4 changes: 2 additions & 2 deletions Sources/UIViewKit/NSObject+Extensions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// NSObject+Extensions.swift
//
// UIViewKit
//
// Created by Blazej SLEBODA on 20/09/2023.
//
Expand All @@ -12,7 +12,7 @@ public protocol NSObjectExtensions {}; extension NSObject: NSObjectExtensions {
extension NSObjectExtensions {

@discardableResult
public func apply(_ block: (Self) -> Void) -> Self {
public func ibApply(_ block: (Self) -> Void) -> Self {
block(self)
return self
}
Expand Down
19 changes: 19 additions & 0 deletions Sources/UIViewKit/UIViewDSL+Attributes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// UIViewDSL+Attributes.swift
//
//
// Created by Blazej SLEBODA on 29/09/2023.
//

import UIKit

extension UIViewDSL where Self: UIView {

@MainActor
@discardableResult
public func ibAttributes(@NSLayoutConstraintBuilder _ block: (Self) -> [NSLayoutConstraint]) -> Self {
let constraintsGenerated = block(self)
UIViewDSLEngine.shared.addConstraints(for: self, constraints: constraintsGenerated)
return self
}
}
Loading

0 comments on commit 9581a5d

Please sign in to comment.