#!/bin/bash # Apple HIG Designer - iOS Component Generator # Generate SwiftUI and UIKit components following Apple Human Interface Guidelines set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Helper functions print_success() { echo -e "${GREEN}✓ $1${NC}" } print_error() { echo -e "${RED}✗ $1${NC}" } print_info() { echo -e "${BLUE}ℹ $1${NC}" } print_warning() { echo -e "${YELLOW}⚠ $1${NC}" } prompt_input() { local prompt="$1" local var_name="$2" local required="${3:-false}" while true; do echo -e "${BLUE}${prompt}${NC}" read -r input if [ -z "$input" ] && [ "$required" = true ]; then print_error "This field is required." continue fi eval "$var_name='$input'" break done } prompt_select() { local prompt="$1" local var_name="$2" shift 2 local options=("$@") echo -e "${BLUE}${prompt}${NC}" PS3="Select (1-${#options[@]}): " select opt in "${options[@]}"; do if [ -n "$opt" ]; then eval "$var_name='$opt'" break else print_error "Invalid selection. Try again." fi done } # Banner echo "" echo "╔════════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ Apple HIG Designer - Component Generator ║" echo "║ ║" echo "╚════════════════════════════════════════════════════════════╝" echo "" # Step 1: Framework print_info "Step 1/6: Framework" prompt_select "Which framework?" FRAMEWORK \ "SwiftUI" \ "UIKit" # Step 2: Component Type print_info "Step 2/6: Component Type" prompt_select "What type of component?" COMPONENT_TYPE \ "Button" \ "List/TableView" \ "Card" \ "Sheet/Modal" \ "Form" \ "NavigationView" \ "TabView" \ "Custom" # Step 3: Component Name print_info "Step 3/6: Component Name" prompt_input "Component name (e.g., UserProfileView):" COMPONENT_NAME true # Step 4: Features print_info "Step 4/6: Features (comma-separated)" echo -e "${BLUE}Select features to include:${NC}" echo " - accessibility (VoiceOver, Dynamic Type)" echo " - darkmode (Semantic colors)" echo " - animations (Standard iOS animations)" echo " - haptics (Haptic feedback)" read -r FEATURES # Step 5: Platform print_info "Step 5/6: Platform" prompt_select "Target platform?" PLATFORM \ "iOS" \ "iOS + iPadOS" \ "iOS + watchOS" \ "All (iOS, iPadOS, macOS, watchOS)" # Step 6: Output Directory print_info "Step 6/6: Output Location" prompt_input "Output directory (default: ./Components):" OUTPUT_DIR OUTPUT_DIR=${OUTPUT_DIR:-"./Components"} # Create output directory mkdir -p "$OUTPUT_DIR" # Generate based on framework case $FRAMEWORK in "SwiftUI") generate_swiftui_component ;; "UIKit") generate_uikit_component ;; esac generate_swiftui_component() { local file_path="$OUTPUT_DIR/$COMPONENT_NAME.swift" cat > "$file_path" << 'EOF' import SwiftUI /// COMPONENT_NAME /// /// Description of what this component does /// Follows Apple Human Interface Guidelines struct COMPONENT_NAME: View { PROPERTIES var body: some View { COMPONENT_BODY } } PREVIEW EOF # Add properties based on component type case $COMPONENT_TYPE in "Button") sed -i 's/PROPERTIES/\/\/ Button properties\n let title: String\n let action: () -> Void/' "$file_path" sed -i 's/COMPONENT_BODY/Button(action: action) {\n Text(title)\n }\n .buttonStyle(.borderedProminent)\n ACCESSIBILITY_MODIFIERS/' "$file_path" ;; "List/TableView") sed -i 's/PROPERTIES/\/\/ List properties\n let items: [String]/' "$file_path" sed -i 's/COMPONENT_BODY/List(items, id: \\.self) { item in\n Text(item)\n }\n .listStyle(.insetGrouped)\n ACCESSIBILITY_MODIFIERS/' "$file_path" ;; "Card") sed -i 's/PROPERTIES/\/\/ Card properties\n let title: String\n let description: String/' "$file_path" sed -i 's/COMPONENT_BODY/VStack(alignment: .leading, spacing: 12) {\n Text(title)\n .font(.headline)\n \n Text(description)\n .font(.subheadline)\n .foregroundColor(.secondary)\n }\n .padding()\n .background(Color(.systemBackground))\n .cornerRadius(12)\n .shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4)\n ACCESSIBILITY_MODIFIERS/' "$file_path" ;; "Sheet/Modal") sed -i 's/PROPERTIES/@State private var isPresented = false/' "$file_path" sed -i 's/COMPONENT_BODY/Button("Show Sheet") {\n isPresented = true\n }\n .sheet(isPresented: $isPresented) {\n NavigationStack {\n Text("Sheet Content")\n .navigationTitle("Title")\n .navigationBarTitleDisplayMode(.inline)\n .toolbar {\n ToolbarItem(placement: .cancellationAction) {\n Button("Cancel") {\n isPresented = false\n }\n }\n }\n }\n .presentationDetents([.medium, .large])\n }/' "$file_path" ;; "NavigationView") sed -i 's/PROPERTIES/\/\/ Navigation properties\n @State private var path = NavigationPath()/' "$file_path" sed -i 's/COMPONENT_BODY/NavigationStack(path: $path) {\n List {\n NavigationLink("Item 1", value: "Detail 1")\n NavigationLink("Item 2", value: "Detail 2")\n }\n .navigationTitle("Title")\n .navigationBarTitleDisplayMode(.large)\n .navigationDestination(for: String.self) { value in\n Text(value)\n }\n }/' "$file_path" ;; "TabView") sed -i 's/PROPERTIES/@State private var selectedTab = 0/' "$file_path" sed -i 's/COMPONENT_BODY/TabView(selection: $selectedTab) {\n Text("Home")\n .tabItem {\n Label("Home", systemImage: "house")\n }\n .tag(0)\n \n Text("Search")\n .tabItem {\n Label("Search", systemImage: "magnifyingglass")\n }\n .tag(1)\n \n Text("Profile")\n .tabItem {\n Label("Profile", systemImage: "person")\n }\n .tag(2)\n }/' "$file_path" ;; *) sed -i 's/PROPERTIES/\/\/ Component properties/' "$file_path" sed -i 's/COMPONENT_BODY/Text("Custom Component")\n .font(.body)\n ACCESSIBILITY_MODIFIERS/' "$file_path" ;; esac # Add accessibility modifiers if requested if [[ $FEATURES == *"accessibility"* ]]; then sed -i 's/ACCESSIBILITY_MODIFIERS/.accessibilityLabel("Component label")\n .accessibilityHint("Component hint")/' "$file_path" else sed -i 's/ACCESSIBILITY_MODIFIERS//' "$file_path" fi # Add preview sed -i "s/PREVIEW/#Preview {\n COMPONENT_NAME()\n}/" "$file_path" # Replace component name sed -i "s/COMPONENT_NAME/$COMPONENT_NAME/g" "$file_path" print_success "Created SwiftUI component: $file_path" # Generate test file if needed if [[ $FEATURES == *"testing"* ]]; then generate_test_file_swiftui fi } generate_uikit_component() { local file_path="$OUTPUT_DIR/$COMPONENT_NAME.swift" cat > "$file_path" << 'EOF' import UIKit /// COMPONENT_NAME /// /// Description of what this component does /// Follows Apple Human Interface Guidelines class COMPONENT_NAME: UIView { // MARK: - Properties PROPERTIES // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) setupView() } required init?(coder: NSCoder) { super.init(coder: coder) setupView() } // MARK: - Setup private func setupView() { SETUP_CODE setupAccessibility() } private func setupAccessibility() { ACCESSIBILITY_SETUP } // MARK: - Layout override func layoutSubviews() { super.layoutSubviews() // Layout code here } } EOF # Add properties based on component type case $COMPONENT_TYPE in "Button") sed -i 's/PROPERTIES/private let button: UIButton = {\n let button = UIButton(type: .system)\n button.translatesAutoresizingMaskIntoConstraints = false\n button.configuration = .filled()\n return button\n }()/' "$file_path" sed -i 's/SETUP_CODE/addSubview(button)\n \n NSLayoutConstraint.activate([\n button.centerXAnchor.constraint(equalTo: centerXAnchor),\n button.centerYAnchor.constraint(equalTo: centerYAnchor),\n button.heightAnchor.constraint(greaterThanOrEqualToConstant: 44)\n ])/' "$file_path" ;; "List/TableView") sed -i 's/PROPERTIES/private let tableView: UITableView = {\n let table = UITableView(frame: .zero, style: .insetGrouped)\n table.translatesAutoresizingMaskIntoConstraints = false\n return table\n }()\n \n private var items: [String] = []/' "$file_path" sed -i 's/SETUP_CODE/addSubview(tableView)\n tableView.delegate = self\n tableView.dataSource = self\n \n NSLayoutConstraint.activate([\n tableView.topAnchor.constraint(equalTo: topAnchor),\n tableView.leadingAnchor.constraint(equalTo: leadingAnchor),\n tableView.trailingAnchor.constraint(equalTo: trailingAnchor),\n tableView.bottomAnchor.constraint(equalTo: bottomAnchor)\n ])/' "$file_path" ;; *) sed -i 's/PROPERTIES/\/\/ Add component properties here/' "$file_path" sed -i 's/SETUP_CODE/\/\/ Setup UI components/' "$file_path" ;; esac # Add accessibility setup if [[ $FEATURES == *"accessibility"* ]]; then sed -i 's/ACCESSIBILITY_SETUP/isAccessibilityElement = true\n accessibilityLabel = "Component label"\n accessibilityHint = "Component hint"\n accessibilityTraits = .button/' "$file_path" else sed -i 's/ACCESSIBILITY_SETUP/\/\/ Configure accessibility/' "$file_path" fi # Replace component name sed -i "s/COMPONENT_NAME/$COMPONENT_NAME/g" "$file_path" print_success "Created UIKit component: $file_path" } generate_test_file_swiftui() { local test_file="$OUTPUT_DIR/${COMPONENT_NAME}Tests.swift" cat > "$test_file" << 'EOF' import XCTest import SwiftUI @testable import YourApp final class COMPONENT_NAMETests: XCTestCase { func testComponentRenders() { let view = COMPONENT_NAME() XCTAssertNotNil(view) } func testAccessibility() { // Test VoiceOver labels // Test Dynamic Type support } } EOF sed -i "s/COMPONENT_NAME/$COMPONENT_NAME/g" "$test_file" print_success "Created test file: $test_file" } # Summary echo "" echo "╔════════════════════════════════════════════════════════════╗" echo "║ Generation Complete ║" echo "╚════════════════════════════════════════════════════════════╝" echo "" print_success "Component: $COMPONENT_NAME" print_success "Framework: $FRAMEWORK" print_success "Type: $COMPONENT_TYPE" print_success "Platform: $PLATFORM" print_success "Location: $OUTPUT_DIR" echo "" print_info "Files created:" echo " - $COMPONENT_NAME.swift" if [[ $FEATURES == *"testing"* ]]; then echo " - ${COMPONENT_NAME}Tests.swift" fi echo "" print_info "Apple HIG Guidelines Applied:" echo " ✓ Minimum tap target: 44x44 points" echo " ✓ System fonts (San Francisco)" echo " ✓ Semantic colors (dark mode support)" if [[ $FEATURES == *"accessibility"* ]]; then echo " ✓ VoiceOver support" echo " ✓ Dynamic Type support" fi if [[ $FEATURES == *"haptics"* ]]; then echo " ✓ Haptic feedback" fi echo "" print_info "Next steps:" echo " 1. Review generated code" echo " 2. Add component to your Xcode project" echo " 3. Customize properties and logic" echo " 4. Test with VoiceOver" echo " 5. Test in light and dark mode" echo " 6. Test with different Dynamic Type sizes" echo ""