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
17 changes: 15 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Build and Test

env:
afterpay-scheme: Afterpay
DEVELOPER_DIR: /Applications/Xcode_16.1.0.app/Contents/Developer

on:
push:
Expand All @@ -17,14 +16,28 @@ jobs:
runs-on: macos-latest

env:
destination: platform=iOS Simulator,name=iPhone 16,OS=18.1
example-scheme: Example
example-ui-test-scheme: ExampleUITests
workspace: Afterpay.xcworkspace

steps:
- uses: actions/checkout@v4

- name: List Available Simulators
run: xcrun simctl list devices available

- name: Set Destination
run: |
# Find any available iPhone simulator
SIMULATOR_NAME=$(xcrun simctl list devices available | grep 'iPhone' | grep -v 'unavailable' | head -1 | sed -E 's/.*iPhone ([0-9]+).*/iPhone \1/')
if [ -z "$SIMULATOR_NAME" ]; then
echo "No iPhone simulator found, falling back to generic iOS Simulator"
echo "destination=platform=iOS Simulator" >> $GITHUB_ENV
else
echo "Using simulator: $SIMULATOR_NAME"
echo "destination=platform=iOS Simulator,name=$SIMULATOR_NAME" >> $GITHUB_ENV
fi

- name: Cache Mint Packages
uses: actions/cache@v4
with:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,5 @@ iOSInjectionProject/

# Generated doc files #
docs/_site
CLAUDE.md
/.claude
308 changes: 246 additions & 62 deletions Example/Example/Components/ComponentsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
embed: contentStack.addArrangedSubview
)

let divider = UIView()
divider.backgroundColor = .separator
divider.translatesAutoresizingMaskIntoConstraints = false
divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
contentStack.addArrangedSubview(divider)

install(
ContentStackViewController(stackTitle: "Dark Theme", userInterfaceStyle: .dark),
embed: contentStack.addArrangedSubview
Expand Down Expand Up @@ -143,7 +149,7 @@
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 8
stack.spacing = 16

let titleLabel = UILabel()
titleLabel.text = stackTitle
Expand All @@ -152,76 +158,215 @@
stack.addArrangedSubview(titleLabel)

if Afterpay.enabled {
// MARK: - BadgeView (not available in Cash App Afterpay regions)
if !Afterpay.isCashAppAfterpayRegion {
let badge = BadgeView(colorScheme: .dynamic(lightPalette: .default, darkPalette: .alt))
badge.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(sectionHeader("BadgeView"))

let badgeDefault = BadgeView(colorScheme: .static(.default))
badgeDefault.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(badgeDefault, caption: "BadgeView - default"))

let badge2 = BadgeView(colorScheme: .dynamic(lightPalette: .darkMono, darkPalette: .lightMono))
badge.widthAnchor.constraint(equalToConstant: 64).isActive = true
let badgeAlt = BadgeView(colorScheme: .static(.alt))
badgeAlt.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(badgeAlt, caption: "BadgeView - alt"))

let badgeStack = UIStackView(arrangedSubviews: [badge, badge2, UIView()])
stack.addArrangedSubview(badgeStack)
let lockup = LockupView(colorScheme: .dynamic(lightPalette: .lightMono, darkPalette: .darkMono))
lockup.widthAnchor.constraint(equalToConstant: 64).isActive = true
let badgeDarkMono = BadgeView(colorScheme: .static(.darkMono))
badgeDarkMono.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(badgeDarkMono, caption: "BadgeView - darkMono"))

let lockup2 = LockupView(colorScheme: .dynamic(lightPalette: .darkMono, darkPalette: .default))
lockup.widthAnchor.constraint(equalToConstant: 64).isActive = true
let lockupStack = UIStackView(arrangedSubviews: [lockup, lockup2, UIView()])
stack.addArrangedSubview(lockupStack)
let badgeLightMono = BadgeView(colorScheme: .static(.lightMono))
badgeLightMono.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(badgeLightMono, caption: "BadgeView - lightMono"))
}

let payButton: UIButton = PaymentButton(
colorScheme: .dynamic(lightPalette: .default, darkPalette: .alt),
buttonKind: .buyNow
)
stack.addArrangedSubview(payButton)
let payButton2: UIButton = PaymentButton(
colorScheme: .dynamic(lightPalette: .lightMono, darkPalette: .darkMono),
buttonKind: .checkout
)
stack.addArrangedSubview(payButton2)
let payButton3: UIButton = PaymentButton(
colorScheme: .dynamic(lightPalette: .alt, darkPalette: .default),
buttonKind: .payNow
)
stack.addArrangedSubview(payButton3)
let payButton4: UIButton = PaymentButton(
colorScheme: .dynamic(lightPalette: .darkMono, darkPalette: .lightMono),
buttonKind: .continueWith
)
stack.addArrangedSubview(payButton4)

let priceBreakdown1 = PriceBreakdownView()
priceBreakdown1.introText = .payInTitle
priceBreakdown1.totalAmount = 103.50
priceBreakdown1.delegate = self
priceBreakdown1.moreInfoOptions = MoreInfoOptions(modalTheme: .white)
priceBreakdown1.logoColorScheme = .dynamic(lightPalette: .darkMono, darkPalette: .alt)

stack.addArrangedSubview(priceBreakdown1)

let priceBreakdown2 = PriceBreakdownView()
priceBreakdown2.totalAmount = 100
priceBreakdown2.delegate = self
priceBreakdown2.showWithText = false
priceBreakdown2.showInterestFreeText = false
priceBreakdown2.logoColorScheme = .dynamic(lightPalette: .default, darkPalette: .lightMono)
priceBreakdown2.moreInfoOptions = MoreInfoOptions(modalLinkStyle: .moreInfoText)
priceBreakdown2.logoType = .compactBadge
stack.addArrangedSubview(priceBreakdown2)

let priceBreakdown3 = PriceBreakdownView()
priceBreakdown3.totalAmount = 3000
priceBreakdown3.delegate = self
priceBreakdown3.logoColorScheme = .dynamic(lightPalette: .darkMono, darkPalette: .lightMono)
stack.addArrangedSubview(priceBreakdown3)
let b = BadgeView(colorScheme: .dynamic(lightPalette: .alt, darkPalette: .darkMono))
stack.addArrangedSubview(b)
// MARK: - LockupView
stack.addArrangedSubview(sectionHeader("LockupView"))

let lockupDefault = LockupView(colorScheme: .static(.default))
lockupDefault.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(lockupDefault, caption: "LockupView - default"))

let lockupAlt = LockupView(colorScheme: .static(.alt))
lockupAlt.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(lockupAlt, caption: "LockupView - alt"))

let lockupDarkMono = LockupView(colorScheme: .static(.darkMono))
lockupDarkMono.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(lockupDarkMono, caption: "LockupView - darkMono"))

let lockupLightMono = LockupView(colorScheme: .static(.lightMono))
lockupLightMono.widthAnchor.constraint(equalToConstant: 64).isActive = true
stack.addArrangedSubview(labeledView(lockupLightMono, caption: "LockupView - lightMono"))

// MARK: - PaymentButton
stack.addArrangedSubview(sectionHeader("PaymentButton"))

// buyNow - all color palettes
let buyNowDefault = PaymentButton(colorScheme: .static(.default), buttonKind: .buyNow)
stack.addArrangedSubview(labeledView(buyNowDefault, caption: "PaymentButton - buyNow, default"))

let buyNowAlt = PaymentButton(colorScheme: .static(.alt), buttonKind: .buyNow)
stack.addArrangedSubview(labeledView(buyNowAlt, caption: "PaymentButton - buyNow, alt"))

let buyNowDarkMono = PaymentButton(colorScheme: .static(.darkMono), buttonKind: .buyNow)
stack.addArrangedSubview(labeledView(buyNowDarkMono, caption: "PaymentButton - buyNow, darkMono"))

let buyNowLightMono = PaymentButton(colorScheme: .static(.lightMono), buttonKind: .buyNow)
stack.addArrangedSubview(labeledView(buyNowLightMono, caption: "PaymentButton - buyNow, lightMono"))

// checkout - all color palettes
let checkoutDefault = PaymentButton(colorScheme: .static(.default), buttonKind: .checkout)
stack.addArrangedSubview(labeledView(checkoutDefault, caption: "PaymentButton - checkout, default"))

let checkoutAlt = PaymentButton(colorScheme: .static(.alt), buttonKind: .checkout)
stack.addArrangedSubview(labeledView(checkoutAlt, caption: "PaymentButton - checkout, alt"))

let checkoutDarkMono = PaymentButton(colorScheme: .static(.darkMono), buttonKind: .checkout)
stack.addArrangedSubview(labeledView(checkoutDarkMono, caption: "PaymentButton - checkout, darkMono"))

let checkoutLightMono = PaymentButton(colorScheme: .static(.lightMono), buttonKind: .checkout)
stack.addArrangedSubview(labeledView(checkoutLightMono, caption: "PaymentButton - checkout, lightMono"))

// payNow - all color palettes
let payNowDefault = PaymentButton(colorScheme: .static(.default), buttonKind: .payNow)
stack.addArrangedSubview(labeledView(payNowDefault, caption: "PaymentButton - payNow, default"))

let payNowAlt = PaymentButton(colorScheme: .static(.alt), buttonKind: .payNow)
stack.addArrangedSubview(labeledView(payNowAlt, caption: "PaymentButton - payNow, alt"))

let payNowDarkMono = PaymentButton(colorScheme: .static(.darkMono), buttonKind: .payNow)
stack.addArrangedSubview(labeledView(payNowDarkMono, caption: "PaymentButton - payNow, darkMono"))

let payNowLightMono = PaymentButton(colorScheme: .static(.lightMono), buttonKind: .payNow)
stack.addArrangedSubview(labeledView(payNowLightMono, caption: "PaymentButton - payNow, lightMono"))

// continueWith - all color palettes
let continueWithDefault = PaymentButton(colorScheme: .static(.default), buttonKind: .continueWith)
stack.addArrangedSubview(labeledView(continueWithDefault, caption: "PaymentButton - continueWith, default"))

let continueWithAlt = PaymentButton(colorScheme: .static(.alt), buttonKind: .continueWith)
stack.addArrangedSubview(labeledView(continueWithAlt, caption: "PaymentButton - continueWith, alt"))

let continueWithDarkMono = PaymentButton(colorScheme: .static(.darkMono), buttonKind: .continueWith)
stack.addArrangedSubview(labeledView(continueWithDarkMono, caption: "PaymentButton - continueWith, darkMono"))

let continueWithLightMono = PaymentButton(colorScheme: .static(.lightMono), buttonKind: .continueWith)
stack.addArrangedSubview(labeledView(continueWithLightMono, caption: "PaymentButton - continueWith, lightMono"))

// MARK: - PriceBreakdownView
stack.addArrangedSubview(sectionHeader("PriceBreakdownView"))

// Logo type variations
let priceBreakdownBadge = PriceBreakdownView()
priceBreakdownBadge.totalAmount = 100
priceBreakdownBadge.delegate = self
priceBreakdownBadge.logoType = .badge
stack.addArrangedSubview(labeledView(priceBreakdownBadge, caption: "PriceBreakdownView - logoType: badge"))

let priceBreakdownLockup = PriceBreakdownView()
priceBreakdownLockup.totalAmount = 100
priceBreakdownLockup.delegate = self
priceBreakdownLockup.logoType = .lockup
stack.addArrangedSubview(labeledView(priceBreakdownLockup, caption: "PriceBreakdownView - logoType: lockup"))

let priceBreakdownCompact = PriceBreakdownView()
priceBreakdownCompact.totalAmount = 100
priceBreakdownCompact.delegate = self
priceBreakdownCompact.logoType = .compactBadge
stack.addArrangedSubview(labeledView(priceBreakdownCompact, caption: "PriceBreakdownView - logoType: compactBadge"))

Check warning on line 276 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 122 characters (line_length)

// Intro text variations
let priceBreakdownOr = PriceBreakdownView()
priceBreakdownOr.totalAmount = 100
priceBreakdownOr.delegate = self
priceBreakdownOr.introText = .or
stack.addArrangedSubview(labeledView(priceBreakdownOr, caption: "PriceBreakdownView - introText: or"))

let priceBreakdownPayIn = PriceBreakdownView()
priceBreakdownPayIn.totalAmount = 100
priceBreakdownPayIn.delegate = self
priceBreakdownPayIn.introText = .payIn
stack.addArrangedSubview(labeledView(priceBreakdownPayIn, caption: "PriceBreakdownView - introText: payIn"))

let priceBreakdownMake = PriceBreakdownView()
priceBreakdownMake.totalAmount = 100
priceBreakdownMake.delegate = self
priceBreakdownMake.introText = .make
stack.addArrangedSubview(labeledView(priceBreakdownMake, caption: "PriceBreakdownView - introText: make"))

let priceBreakdownPay = PriceBreakdownView()
priceBreakdownPay.totalAmount = 100
priceBreakdownPay.delegate = self
priceBreakdownPay.introText = .pay
stack.addArrangedSubview(labeledView(priceBreakdownPay, caption: "PriceBreakdownView - introText: pay"))

let priceBreakdownIn = PriceBreakdownView()
priceBreakdownIn.totalAmount = 100
priceBreakdownIn.delegate = self
priceBreakdownIn.introText = .in
stack.addArrangedSubview(labeledView(priceBreakdownIn, caption: "PriceBreakdownView - introText: in"))

let priceBreakdownEmpty = PriceBreakdownView()
priceBreakdownEmpty.totalAmount = 100
priceBreakdownEmpty.delegate = self
priceBreakdownEmpty.introText = .empty
stack.addArrangedSubview(labeledView(priceBreakdownEmpty, caption: "PriceBreakdownView - introText: empty"))

// Display options
let priceBreakdownNoWith = PriceBreakdownView()
priceBreakdownNoWith.totalAmount = 100
priceBreakdownNoWith.delegate = self
priceBreakdownNoWith.showWithText = false
stack.addArrangedSubview(labeledView(priceBreakdownNoWith, caption: "PriceBreakdownView - showWithText: false"))

let priceBreakdownNoInterest = PriceBreakdownView()
priceBreakdownNoInterest.totalAmount = 100
priceBreakdownNoInterest.delegate = self
priceBreakdownNoInterest.showInterestFreeText = false
stack.addArrangedSubview(labeledView(priceBreakdownNoInterest, caption: "PriceBreakdownView - showInterestFreeText: false"))

Check warning on line 326 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 130 characters (line_length)

let priceBreakdownMinimal = PriceBreakdownView()
priceBreakdownMinimal.totalAmount = 100
priceBreakdownMinimal.delegate = self
priceBreakdownMinimal.showWithText = false
priceBreakdownMinimal.showInterestFreeText = false
stack.addArrangedSubview(labeledView(priceBreakdownMinimal, caption: "PriceBreakdownView - minimal (no with/interest)"))

Check warning on line 333 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 126 characters (line_length)

// Amount edge cases
let priceBreakdownOutOfRange = PriceBreakdownView()
priceBreakdownOutOfRange.totalAmount = 5000
priceBreakdownOutOfRange.delegate = self
stack.addArrangedSubview(labeledView(priceBreakdownOutOfRange, caption: "PriceBreakdownView - amount out of range"))

Check warning on line 339 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 122 characters (line_length)

// Logo color schemes
let priceBreakdownLogoDefault = PriceBreakdownView()
priceBreakdownLogoDefault.totalAmount = 100
priceBreakdownLogoDefault.delegate = self
priceBreakdownLogoDefault.logoColorScheme = .static(.default)
stack.addArrangedSubview(labeledView(priceBreakdownLogoDefault, caption: "PriceBreakdownView - logoColorScheme: default"))

Check warning on line 346 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 128 characters (line_length)

let priceBreakdownLogoAlt = PriceBreakdownView()
priceBreakdownLogoAlt.totalAmount = 100
priceBreakdownLogoAlt.delegate = self
priceBreakdownLogoAlt.logoColorScheme = .static(.alt)
stack.addArrangedSubview(labeledView(priceBreakdownLogoAlt, caption: "PriceBreakdownView - logoColorScheme: alt"))

let priceBreakdownLogoDarkMono = PriceBreakdownView()
priceBreakdownLogoDarkMono.totalAmount = 100
priceBreakdownLogoDarkMono.delegate = self
priceBreakdownLogoDarkMono.logoColorScheme = .static(.darkMono)
stack.addArrangedSubview(labeledView(priceBreakdownLogoDarkMono, caption: "PriceBreakdownView - logoColorScheme: darkMono"))

Check warning on line 358 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 130 characters (line_length)

let priceBreakdownLogoLightMono = PriceBreakdownView()
priceBreakdownLogoLightMono.totalAmount = 100
priceBreakdownLogoLightMono.delegate = self
priceBreakdownLogoLightMono.logoColorScheme = .static(.lightMono)
stack.addArrangedSubview(labeledView(priceBreakdownLogoLightMono, caption: "PriceBreakdownView - logoColorScheme: lightMono"))

Check warning on line 364 in Example/Example/Components/ComponentsViewController.swift

View workflow job for this annotation

GitHub Actions / Build and Test

Line should be 120 characters or less; currently it has 132 characters (line_length)
}

let stackConstraints = [
stack.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
stack.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
stack.topAnchor.constraint(equalTo: view.topAnchor, constant: 8),
stack.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8),
]
Expand All @@ -234,4 +379,43 @@

func viewControllerForPresentation() -> UIViewController { self }

private func sectionHeader(_ title: String) -> UIView {
let container = UIView()

let label = UILabel()
label.text = title
label.font = .preferredFont(forTextStyle: .headline)
label.textColor = .label
label.adjustsFontForContentSizeCategory = true
label.translatesAutoresizingMaskIntoConstraints = false

container.addSubview(label)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: container.leadingAnchor),
label.trailingAnchor.constraint(equalTo: container.trailingAnchor),
label.topAnchor.constraint(equalTo: container.topAnchor, constant: 16),
label.bottomAnchor.constraint(equalTo: container.bottomAnchor),
])

return container
}

private func labeledView(_ view: UIView, caption: String) -> UIView {
let container = UIStackView()
container.axis = .vertical
container.alignment = .leading
container.spacing = 4

let label = UILabel()
label.text = caption
label.font = .preferredFont(forTextStyle: .caption1)
label.textColor = .secondaryLabel
label.adjustsFontForContentSizeCategory = true

container.addArrangedSubview(view)
container.addArrangedSubview(label)

return container
}

}