Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/run-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Xcode Build and Test

on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
workflow_dispatch:

jobs:
test:
name: Run Xcode Tests on iPhone 16 (iOS 18.1)
# macOS-latest should ideally include Xcode 16+, but we'll ensure it.
runs-on: macos-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Select Xcode 16.x for iOS 18.1 support
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 'latest-stable' # Try 'latest-stable' first

- name: Display active Xcode version after selection
run: xcodebuild -version
# Verify here that Xcode 16.x (or newer) is now active.

- name: List available iOS Simulators and their OS versions
# Now, this should show iPhone 16 with iOS 18.x if Xcode 16 is active.
run: xcrun simctl list devices

- name: Clean Xcode Derived Data
# Always good practice for CI to ensure a fresh build.
run: xcodebuild clean -scheme BuilderIO -destination "platform=iOS Simulator,name=iPhone 16,OS=18.1"

- name: Build and Test Xcode Project on iPhone 16 (iOS 18.1)
run: |
SIMULATOR_NAME="iPhone 16"
# Ensure this OS version is available with your selected Xcode 16.x
OS_VERSION="18.1"

DESTINATION="platform=iOS Simulator,name=${SIMULATOR_NAME},OS=${OS_VERSION}"

echo "Attempting to build and test on destination: $DESTINATION"

xcodebuild test -scheme BuilderIO \
-destination "$DESTINATION" \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO
# If you want to capture test results:
# -resultBundlePath "output/TestResults.xcresult"
22 changes: 12 additions & 10 deletions .github/workflows/swift-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Swift
uses: swift-actions/setup-swift@v1
- name: Select Xcode 16.x for swift-format support
uses: maxim-lobanov/setup-xcode@v1
with:
swift-version: "5.9"
xcode-version: 'latest-stable' # Aims for Xcode 16.x or newer

- name: Build swift-format
- name: Verify Xcode version and swift-format availability
run: |
git clone https://github.com/apple/swift-format.git
cd swift-format
swift build -c release
echo "$(pwd)/.build/release" >> $GITHUB_PATH
echo "Active Xcode version:"
xcodebuild -version
echo "swift-format path (should be within Xcode toolchain):"
xcrun --find swift-format || echo "swift-format not found via xcrun, check Xcode version."


- name: Run swift-format lint (will fail on issues)
run: |
swift-format lint --recursive Sources Tests
xcrun swift-format lint --recursive Sources Tests --ignore-unparsable-files

45 changes: 45 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 28 additions & 10 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,51 @@
import PackageDescription

let package = Package(
name: "BuilderIO",
name: "BuilderIO", // Good: Clear, concise name matching the library

platforms: [
.iOS(.v17),
.iOS(.v17),
],

// --- Products ---
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "BuilderIO",
targets: ["BuilderIO"]
),
],

// --- Dependencies ---
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.1"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.17.0"),
.package(url: "https://github.com/WeTransfer/Mocker.git", .upToNextMajor(from: "3.0.0")),
],

// --- Targets ---
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
// Main library target
.target(
name: "BuilderIO",
dependencies: [
"SwiftyJSON",
],
resources: [
.process("Resources/Fonts")
]
.process("Resources/Fonts")
]
),

// Test target
.testTarget(
name: "BuilderIOTests",
dependencies: [
"BuilderIO", // Dependency on the local library target
"Mocker",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
],
resources: [
.process("Resources")
]
),
]
)
)
4 changes: 3 additions & 1 deletion Sources/BuilderIO/Components/BuilderBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ struct BuilderBlockLayout<Content: View>: View {
// 1. Extract basic layout parameters
let direction = responsiveStyles["flexDirection"] ?? "column"
let wrap = responsiveStyles["flexWrap"] == "wrap" && direction == "row"
let scroll = (responsiveStyles["overflow"] == "auto" || responsiveStyles["overflow"] == "scroll") && direction == "row"
let scroll =
(responsiveStyles["overflow"] == "auto" || responsiveStyles["overflow"] == "scroll")
&& direction == "row"

let justify = responsiveStyles["justifyContent"]
let alignItems = responsiveStyles["alignItems"]
Expand Down
4 changes: 2 additions & 2 deletions Sources/BuilderIO/Extensions/Font.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ extension Font {
_ = UIFont.registerFont(bundle: .module, fontName: "DMSans", fontExtension: "ttf")
_ = UIFont.registerFont(bundle: .module, fontName: "DMSans-Italic", fontExtension: "ttf")
}

}

extension UIFont {

static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) -> Bool {
guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension) else {
fatalError("Couldn't find font \(fontName)")
Expand Down
78 changes: 78 additions & 0 deletions Tests/BuilderIOTests/BuilderIOPageViewTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import XCTest
import BuilderIO
import SnapshotTesting
import UIKit
import SwiftUI

@MainActor
class BuilderIOPageViewTests: XCTestCase {

static let record = false

override func setUpWithError() throws {
BuilderIOManager.configure(apiKey: "UNITTESTINGAPIKEY", customNavigationScheme: "builderio")

continueAfterFailure = false
}

override func tearDownWithError() throws {
// Clear all mocks after each test
print(" 🚨 Tests deregistered")

BuilderIOMockManager.shared.clearAllMocks()
try super.tearDownWithError()
}


func testTextView() throws {
BuilderIOMockManager.shared.registerMock(for: "/text", with: "text", statusCode: 200)

let hostingController = makeHostingController(for: "/text", width: 375, height: 812)

      let expectation = XCTestExpectation(description: "Wait for view to render")

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

assertSnapshot(matching: hostingController, as: .image, record: BuilderIOPageViewTests.record)
expectation.fulfill()
}


wait(for: [expectation], timeout: 3)
}

func testLayoutsView() throws {
BuilderIOMockManager.shared.registerMock(for: "/layout", with: "layout", statusCode: 200)

let hostingController = makeHostingController(for: "/layout", width: 375, height: 812)

      let expectation = XCTestExpectation(description: "Wait for view to render")

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

assertSnapshot(matching: hostingController, as: .image, record: BuilderIOPageViewTests.record)
expectation.fulfill()
}


wait(for: [expectation], timeout: 3)
}

/// - Returns: A UIHostingController wrapping the SwiftUI Text view.
func makeHostingController(for url: String, width: CGFloat, height: CGFloat) -> UIHostingController<some View> {
let view = BuilderIOPage(url: url, onClickEventHandler: { event in
print("Handle Event Action")
})

let hostingController = UIHostingController(rootView: view.frame(width: 375, height: 812))
hostingController.view.frame = CGRect(x: 0, y: 0, width: 375, height: 812)

let window = UIWindow(frame: hostingController.view.frame)
window.rootViewController = hostingController
window.makeKeyAndVisible()


return hostingController
}

}
Loading
Loading