Files
2025-11-29 18:48:55 +08:00

394 lines
12 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Apple HIG Designer - Design Validation
# Validate Swift code against 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'
MAGENTA='\033[0;35m'
NC='\033[0m' # No Color
# Counters
PASS_COUNT=0
FAIL_COUNT=0
WARNING_COUNT=0
# Helper functions
print_success() {
echo -e "${GREEN}✓ PASS${NC} $1"
((PASS_COUNT++))
}
print_error() {
echo -e "${RED}✗ FAIL${NC} $1"
((FAIL_COUNT++))
}
print_warning() {
echo -e "${YELLOW}⚠ WARN${NC} $1"
((WARNING_COUNT++))
}
print_info() {
echo -e "${BLUE} INFO${NC} $1"
}
print_section() {
echo ""
echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${MAGENTA}$1${NC}"
echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
# Banner
echo ""
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ Apple HIG Designer - Design Validation ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""
# Get target
if [ -z "$1" ]; then
print_info "Usage: $0 <file.swift|directory>"
print_info "Example: $0 ContentView.swift"
print_info "Example: $0 Views/"
exit 1
fi
TARGET="$1"
# Check if target exists
if [ ! -e "$TARGET" ]; then
print_error "Target not found: $TARGET"
exit 1
fi
# Section 1: Typography
print_section "1. TYPOGRAPHY"
check_system_fonts() {
# Check for system font usage
if grep -q '\.font(' "$1"; then
if grep -q '\.font(.body)\|\.font(.title)\|\.font(.headline)' "$1"; then
print_success "Using system text styles (Dynamic Type)"
else
print_warning "Consider using system text styles (.body, .headline, etc.)"
fi
fi
# Check for hard-coded font sizes
if grep -q 'Font.system(size:' "$1"; then
print_warning "Hard-coded font sizes found - consider Dynamic Type"
echo " Tip: Use .font(.body) instead of .font(.system(size: 17))"
fi
}
# Section 2: Colors
print_section "2. COLORS & DARK MODE"
check_semantic_colors() {
# Check for semantic color usage
if grep -q 'Color(.label)\|Color(.systemBackground)\|Color(.secondary)' "$1"; then
print_success "Using semantic colors (dark mode support)"
else
if grep -q 'Color(' "$1"; then
print_warning "Consider using semantic colors for dark mode support"
echo " Use: Color(.label) instead of Color.black"
fi
fi
# Check for hard-coded colors
if grep -q '#[0-9A-Fa-f]\{6\}\|Color.black\|Color.white' "$1"; then
print_error "Hard-coded colors found - use semantic colors"
echo " Fix: Use Color(.label), Color(.systemBackground), etc."
fi
}
# Section 3: Accessibility
print_section "3. ACCESSIBILITY"
check_accessibility_labels() {
# Check for accessibility labels
if grep -q 'Image(systemName:' "$1"; then
if grep -q '\.accessibilityLabel' "$1"; then
print_success "SF Symbols have accessibility labels"
else
print_error "SF Symbols missing accessibility labels"
echo " Fix: Add .accessibilityLabel(\"Description\")"
fi
fi
# Check for VoiceOver support
if grep -q '\.accessibilityLabel\|\.accessibilityHint\|\.accessibilityValue' "$1"; then
print_success "VoiceOver support implemented"
else
print_warning "Consider adding VoiceOver support"
fi
}
# Section 4: Touch Targets
print_section "4. TOUCH TARGETS"
check_touch_targets() {
# Check for minimum tap targets
if grep -q '\.frame(.*height.*44\|minHeight.*44' "$1"; then
print_success "Minimum touch target size (44pt) specified"
else
if grep -q 'Button\|.onTapGesture' "$1"; then
print_warning "Verify touch targets are at least 44x44 points"
echo " Tip: .frame(minWidth: 44, minHeight: 44)"
fi
fi
}
# Section 5: Spacing
print_section "5. SPACING & LAYOUT"
check_spacing() {
# Check for 8pt grid usage
if grep -q '\.padding([0-9]*)\|\.spacing([0-9]*)' "$1"; then
# Extract padding values
paddings=$(grep -o '\.padding([0-9]*)' "$1" | grep -o '[0-9]*')
invalid=false
for pad in $paddings; do
if [ $((pad % 8)) -ne 0 ]; then
invalid=true
break
fi
done
if [ "$invalid" = false ]; then
print_success "Following 8pt grid system"
else
print_warning "Consider using 8pt grid (8, 16, 24, 32, etc.)"
fi
fi
# Check for safe area usage
if grep -q '\.ignoresSafeArea' "$1"; then
print_warning "Ignoring safe areas - ensure intentional"
echo " Tip: Respect safe areas for better device compatibility"
fi
}
# Section 6: Navigation
print_section "6. NAVIGATION"
check_navigation() {
# Check for navigation best practices
if grep -q 'NavigationStack\|NavigationView' "$1"; then
if grep -q '\.navigationTitle' "$1"; then
print_success "Navigation titles present"
else
print_error "NavigationStack missing .navigationTitle"
fi
# Check for navigation bar mode
if grep -q '\.navigationBarTitleDisplayMode(.large)' "$1"; then
print_success "Using large titles for top-level views"
fi
fi
# Check for TabView
if grep -q 'TabView' "$1"; then
# Count tabItems
tab_count=$(grep -c '\.tabItem' "$1")
if [ "$tab_count" -ge 3 ] && [ "$tab_count" -le 5 ]; then
print_success "TabView has appropriate number of tabs ($tab_count)"
elif [ "$tab_count" -gt 5 ]; then
print_error "Too many tabs ($tab_count) - maximum 5 recommended"
elif [ "$tab_count" -lt 3 ]; then
print_warning "Consider if TabView is appropriate for $tab_count tabs"
fi
fi
}
# Section 7: Buttons
print_section "7. BUTTONS & CONTROLS"
check_buttons() {
# Check for button styles
if grep -q 'Button(' "$1"; then
if grep -q '\.buttonStyle' "$1"; then
print_success "Using button styles"
# Check for button hierarchy
if grep -q '\.buttonStyle(.borderedProminent)' "$1"; then
prominent_count=$(grep -c '\.buttonStyle(.borderedProminent)' "$1")
if [ "$prominent_count" -eq 1 ]; then
print_success "Single prominent button (good hierarchy)"
else
print_warning "Multiple prominent buttons - ensure clear hierarchy"
fi
fi
else
print_warning "Consider using .buttonStyle for consistent appearance"
fi
# Check button labels
if grep -q 'Button("' "$1"; then
# Extract button texts
if grep -q 'Button("[A-Z]' "$1"; then
print_success "Button labels start with capital letters"
fi
fi
fi
}
# Section 8: Lists
print_section "8. LISTS & TABLES"
check_lists() {
# Check for list styles
if grep -q 'List' "$1"; then
if grep -q '\.listStyle' "$1"; then
if grep -q '\.listStyle(.insetGrouped)' "$1"; then
print_success "Using iOS standard .insetGrouped list style"
fi
else
print_warning "Consider specifying .listStyle(.insetGrouped)"
fi
fi
}
# Section 9: Animations
print_section "9. ANIMATIONS & MOTION"
check_animations() {
# Check for spring animations
if grep -q 'withAnimation' "$1"; then
if grep -q '\.spring()\|\.easeInOut' "$1"; then
print_success "Using iOS-style animations"
fi
fi
# Check for reduce motion
if grep -q '@Environment(.*accessibilityReduceMotion)' "$1"; then
print_success "Respecting Reduce Motion preference"
else
if grep -q 'withAnimation\|\.animation' "$1"; then
print_warning "Consider respecting Reduce Motion accessibility setting"
echo " Tip: @Environment(\\.accessibilityReduceMotion) var reduceMotion"
fi
fi
}
# Section 10: SF Symbols
print_section "10. SF SYMBOLS"
check_sf_symbols() {
# Check for SF Symbols usage
if grep -q 'Image(systemName:' "$1"; then
print_success "Using SF Symbols"
# Check for proper sizing
if grep -q '\.font(.title)\|\.imageScale' "$1"; then
print_success "SF Symbols are properly sized"
else
print_warning "Consider sizing SF Symbols with .font() or .imageScale()"
fi
fi
# Check for custom images that should be SF Symbols
common_icons="heart|star|person|home|settings|search|share"
if grep -E "Image\\(\"($common_icons)" "$1"; then
print_warning "Consider using SF Symbols instead of custom icons"
fi
}
# Run checks on files
if [ -f "$TARGET" ]; then
# Single file
if [[ "$TARGET" == *.swift ]]; then
check_system_fonts "$TARGET"
check_semantic_colors "$TARGET"
check_accessibility_labels "$TARGET"
check_touch_targets "$TARGET"
check_spacing "$TARGET"
check_navigation "$TARGET"
check_buttons "$TARGET"
check_lists "$TARGET"
check_animations "$TARGET"
check_sf_symbols "$TARGET"
else
print_error "File is not a Swift file: $TARGET"
exit 1
fi
elif [ -d "$TARGET" ]; then
# Directory - find Swift files
swift_files=$(find "$TARGET" -name "*.swift" 2>/dev/null)
if [ -z "$swift_files" ]; then
print_error "No Swift files found in $TARGET"
exit 1
fi
for file in $swift_files; do
print_info "Checking: $file"
check_system_fonts "$file"
check_semantic_colors "$file"
check_accessibility_labels "$file"
check_touch_targets "$file"
check_spacing "$file"
echo ""
done
fi
# Summary
echo ""
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ Validation Summary ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""
echo -e "${GREEN}✓ Passed: $PASS_COUNT${NC}"
echo -e "${RED}✗ Failed: $FAIL_COUNT${NC}"
echo -e "${YELLOW}⚠ Warnings: $WARNING_COUNT${NC}"
echo ""
# Calculate score
TOTAL=$((PASS_COUNT + FAIL_COUNT))
if [ $TOTAL -gt 0 ]; then
SCORE=$(( (PASS_COUNT * 100) / TOTAL ))
echo "HIG Compliance Score: $SCORE%"
echo ""
if [ $SCORE -ge 90 ]; then
echo -e "${GREEN}Excellent! Your design follows Apple HIG.${NC}"
elif [ $SCORE -ge 70 ]; then
echo -e "${YELLOW}Good, but needs improvements.${NC}"
else
echo -e "${RED}Needs significant improvements to match Apple HIG.${NC}"
fi
fi
echo ""
print_info "Apple HIG Recommendations:"
echo " 1. Use system fonts with Dynamic Type"
echo " 2. Use semantic colors for dark mode"
echo " 3. Add VoiceOver labels to all images"
echo " 4. Ensure 44x44pt minimum touch targets"
echo " 5. Follow 8pt grid system"
echo " 6. Use large titles for top-level navigation"
echo " 7. Limit TabView to 3-5 tabs"
echo " 8. Use .borderedProminent for primary actions only"
echo ""
print_info "Resources:"
echo " - Apple HIG: https://developer.apple.com/design/human-interface-guidelines/"
echo " - SF Symbols: https://developer.apple.com/sf-symbols/"
echo " - WWDC Videos: https://developer.apple.com/videos/"
echo ""
# Exit code based on failures
if [ $FAIL_COUNT -gt 0 ]; then
exit 1
else
exit 0
fi