Files
daily-notification-plugin/scripts/build-native.sh
Matthew Raymer 8ded555a21 fix(ios): resolve compilation errors and enable successful build
Fixed critical compilation errors preventing iOS plugin build:
- Updated logger API calls from logger.debug(TAG, msg) to logger.log(.debug, msg)
  across all iOS plugin files to match DailyNotificationLogger interface
- Fixed async/await concurrency in makeConditionalRequest using semaphore pattern
- Fixed NotificationContent immutability by creating new instances instead of mutation
- Changed private access control to internal for extension-accessible methods
- Added iOS 15.0+ availability checks for interruptionLevel property
- Fixed static member references using Self.MEMBER_NAME syntax
- Added missing .scheduling case to exhaustive switch statement
- Fixed variable initialization in retry state closures

Added DailyNotificationStorage.swift implementation matching Android pattern.

Updated build scripts with improved error reporting and full log visibility.

iOS plugin now compiles successfully. All build errors resolved.
2025-11-04 22:22:02 -08:00

658 lines
23 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
# Exit on error
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
# Logging functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Validation functions
check_command() {
if ! command -v $1 &> /dev/null; then
log_error "$1 is not installed. Please install it first."
exit 1
fi
}
check_environment() {
local platform=$1
# Initialize NVM if available (for Node.js version management)
if [ -s "$HOME/.nvm/nvm.sh" ]; then
log_info "Loading NVM..."
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
# Use default Node.js version if available
if [ -f "$NVM_DIR/alias/default" ]; then
DEFAULT_NODE=$(cat "$NVM_DIR/alias/default")
if [ -n "$DEFAULT_NODE" ]; then
nvm use default >/dev/null 2>&1 || true
fi
fi
# Use latest LTS if no default
if ! command -v node &> /dev/null; then
log_info "No default Node.js version set, using latest LTS..."
nvm use --lts >/dev/null 2>&1 || nvm install --lts >/dev/null 2>&1 || true
fi
fi
# Common checks
check_command "node"
check_command "npm"
# Check Node.js version
NODE_VERSION=$(node -v | cut -d. -f1 | tr -d 'v')
if [ -z "$NODE_VERSION" ] || ! [[ "$NODE_VERSION" =~ ^[0-9]+$ ]]; then
log_error "Could not determine Node.js version"
log_error "Please install Node.js: nvm install --lts (if using NVM)"
exit 1
fi
if [ "$NODE_VERSION" -lt 14 ]; then
log_error "Node.js version 14 or higher is required (found: $NODE_VERSION)"
exit 1
fi
# Platform-specific checks
case $platform in
"android")
check_environment_android
;;
"ios")
check_environment_ios
;;
"all")
check_environment_android
check_environment_ios
;;
*)
log_error "Invalid platform: $platform"
exit 1
;;
esac
}
check_environment_android() {
log_step "Checking Android environment..."
check_command "java"
# Check for Gradle Wrapper instead of system gradle
if [ ! -f "android/gradlew" ]; then
log_error "Gradle wrapper not found at android/gradlew"
exit 1
fi
# Check Java version (more robust parsing)
JAVA_VERSION_OUTPUT=$(java -version 2>&1 | head -n 1)
if [ -z "$JAVA_VERSION_OUTPUT" ]; then
log_error "Could not determine Java version"
exit 1
fi
# Try multiple parsing methods for different Java output formats
JAVA_VERSION=$(echo "$JAVA_VERSION_OUTPUT" | grep -oE 'version "([0-9]+)' | grep -oE '[0-9]+' | head -1)
# Fallback: try to extract from "openjdk version" or "java version" format
if [ -z "$JAVA_VERSION" ]; then
JAVA_VERSION=$(echo "$JAVA_VERSION_OUTPUT" | sed -E 's/.*version "([0-9]+).*/\1/' | head -1)
fi
# Validate we got a number
if [ -z "$JAVA_VERSION" ] || ! [[ "$JAVA_VERSION" =~ ^[0-9]+$ ]]; then
log_error "Could not parse Java version from: $JAVA_VERSION_OUTPUT"
log_error "Please ensure Java is installed correctly"
exit 1
fi
if [ "$JAVA_VERSION" -lt 11 ]; then
log_error "Java version 11 or higher is required (found: $JAVA_VERSION)"
exit 1
fi
# Check for Android SDK
if [ -z "$ANDROID_HOME" ]; then
log_error "ANDROID_HOME environment variable is not set"
log_error "Set it with: export ANDROID_HOME=/path/to/android/sdk"
exit 1
fi
log_info "✓ Android environment OK (Java $JAVA_VERSION)"
}
check_environment_ios() {
log_step "Checking iOS environment..."
# Check for Xcode command line tools
if ! command -v xcodebuild &> /dev/null; then
log_error "xcodebuild not found. Install Xcode Command Line Tools:"
log_error " xcode-select --install"
exit 1
fi
# Check for CocoaPods
if ! command -v pod &> /dev/null; then
log_error "CocoaPods not found. Install with:"
log_info " gem install cocoapods"
# Check if rbenv is available and suggest reloading
if [ -n "$RBENV_ROOT" ] || [ -d "$HOME/.rbenv" ]; then
log_info "Or if using rbenv, ensure shell is reloaded:"
log_info " source ~/.zshrc # or source ~/.bashrc"
log_info " gem install cocoapods"
fi
# Check if setup script exists
if [ -f "$SCRIPT_DIR/setup-ruby.sh" ]; then
log_info ""
log_info "You can also run the setup script first:"
log_info " ./scripts/setup-ruby.sh"
log_info " gem install cocoapods"
fi
exit 1
fi
# Check for Swift
if ! command -v swift &> /dev/null; then
log_error "Swift compiler not found"
exit 1
fi
# Verify workspace exists
if [ ! -d "ios/DailyNotificationPlugin.xcworkspace" ]; then
log_error "iOS workspace not found: ios/DailyNotificationPlugin.xcworkspace"
exit 1
fi
log_info "✓ iOS environment OK"
}
# Build functions
build_typescript() {
log_info "Building TypeScript..."
# Ensure npm dependencies are installed
if [ ! -d "node_modules" ]; then
log_step "Installing npm dependencies..."
if ! npm install; then
log_error "Failed to install npm dependencies"
exit 1
fi
else
# Check if package.json changed (compare with package-lock.json)
if [ -f "package-lock.json" ] && [ "package.json" -nt "package-lock.json" ]; then
log_step "package.json changed, updating dependencies..."
if ! npm install; then
log_error "Failed to update npm dependencies"
exit 1
fi
fi
fi
npm run clean
if ! npm run build; then
log_error "TypeScript build failed"
exit 1
fi
}
build_plugin_for_test_app() {
log_info "Building plugin for test app..."
# Build the plugin AAR
log_info "Building plugin AAR..."
cd android || exit 1
if ! ./gradlew :plugin:clean :plugin:assembleDebug; then
log_error "Plugin build failed"
exit 1
fi
AAR_FILE="plugin/build/outputs/aar/plugin-debug.aar"
if [ ! -f "$AAR_FILE" ]; then
log_error "AAR file not found at $AAR_FILE"
exit 1
fi
log_info "Plugin AAR built: $AAR_FILE"
# Remove any stale AAR from test app's libs directory
if [ -f "../test-apps/daily-notification-test/android/app/libs/plugin-debug.aar" ]; then
log_info "Removing stale AAR from test app libs..."
rm "../test-apps/daily-notification-test/android/app/libs/plugin-debug.aar"
fi
cd ..
# Ensure symlink is in place
SYMLINK="test-apps/daily-notification-test/node_modules/@timesafari/daily-notification-plugin"
if [ ! -L "$SYMLINK" ] || [ ! -e "$SYMLINK" ]; then
log_info "Creating symlink to plugin..."
mkdir -p "$(dirname "$SYMLINK")"
rm -f "$SYMLINK"
ln -sf "../../../" "$SYMLINK"
fi
# Build test app
log_info "Building test app..."
cd test-apps/daily-notification-test/android || exit 1
if ! ./gradlew clean assembleDebug; then
log_error "Test app build failed"
exit 1
fi
APK_FILE="app/build/outputs/apk/debug/app-debug.apk"
if [ ! -f "$APK_FILE" ]; then
log_error "APK file not found at $APK_FILE"
exit 1
fi
log_info "Test app build successful: $APK_FILE"
log_info "Install with: adb install -r $APK_FILE"
cd ../../..
}
build_android() {
log_info "Building Android..."
# Detect project type
if [ -d "test-apps/daily-notification-test" ]; then
log_info "Detected test app. Building plugin and test app together..."
build_plugin_for_test_app
return 0
fi
cd android || exit 1
# Check if this is a plugin development project
if [ ! -f "capacitor-cordova-android-plugins/cordova.variables.gradle" ]; then
log_warn "This appears to be a plugin development project"
log_warn "Android test app not properly initialized"
# =============================================================================
# AUTOMATIC FIX: capacitor.build.gradle for Plugin Development Projects
# =============================================================================
if [ -f "app/capacitor.build.gradle" ]; then
if grep -q "^apply from: \"../capacitor-cordova-android-plugins/cordova.variables.gradle\"" "app/capacitor.build.gradle"; then
log_info "🔧 Applying automatic fix to capacitor.build.gradle..."
log_info " Reason: Plugin development project missing Capacitor integration files"
log_info " Fix: Commenting out problematic 'apply from' line"
# Apply the fix by commenting out the problematic line
# Handle macOS vs Linux sed differences
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS sed requires empty string after -i
sed -i '' 's/^apply from: "\.\.\/capacitor-cordova-android-plugins\/cordova\.variables\.gradle"/\/\/ Plugin development project - no Capacitor integration files needed\n\/\/ &/' "app/capacitor.build.gradle"
else
# Linux sed
sed -i 's/^apply from: "\.\.\/capacitor-cordova-android-plugins\/cordova\.variables\.gradle"/\/\/ Plugin development project - no Capacitor integration files needed\n\/\/ &/' "app/capacitor.build.gradle"
fi
log_info "✅ Fix applied successfully"
log_warn "⚠️ Note: This fix will be lost if you run 'npx cap sync' or 'npx cap update'"
log_info "💡 To restore the fix later, run this build script again or use: ./scripts/fix-capacitor-build.sh"
else
log_info " capacitor.build.gradle already has the fix applied"
fi
else
log_warn "⚠️ capacitor.build.gradle not found - this may indicate a different project structure"
fi
log_warn "Plugin source code has been built successfully"
log_warn "To test the plugin, use it in a Capacitor app instead"
cd ..
return 0
fi
# Check for gradle wrapper
if [ ! -f "./gradlew" ]; then
log_error "Gradle wrapper not found"
exit 1
fi
# Clean and build
if ! ./gradlew clean; then
log_error "Android clean failed"
exit 1
fi
if ! ./gradlew assembleRelease; then
log_error "Android build failed"
exit 1
fi
# Verify AAR file exists
AAR_FILE="capacitor-cordova-android-plugins/build/outputs/aar/capacitor-cordova-android-plugins-release.aar"
if [ ! -f "$AAR_FILE" ]; then
log_error "AAR file not found at $AAR_FILE"
exit 1
fi
log_info "Android build successful: $AAR_FILE"
cd ..
}
build_ios() {
log_info "Building iOS..."
cd ios || exit 1
# Build configuration (define early for validation)
SCHEME="DailyNotificationPlugin"
CONFIG="Release"
WORKSPACE="DailyNotificationPlugin.xcworkspace"
# Install CocoaPods dependencies
log_step "Installing CocoaPods dependencies..."
if [ ! -f "Podfile.lock" ] || [ "Podfile" -nt "Podfile.lock" ] || [ ! -d "Pods" ] || [ ! -d "Pods/Target Support Files" ]; then
log_info "Podfile changed, Podfile.lock missing, or Pods incomplete - running pod install..."
if ! pod install --repo-update; then
log_error "Failed to install CocoaPods dependencies"
exit 1
fi
else
log_info "Podfile.lock is up to date and Pods directory exists, skipping pod install"
fi
# Quick Swift syntax validation (full validation happens during build)
log_step "Validating Swift syntax..."
cd Plugin
SWIFT_FILES=$(find . -name "*.swift" -type f 2>/dev/null)
if [ -z "$SWIFT_FILES" ]; then
log_warn "No Swift files found in Plugin directory"
else
# Use swiftc with iOS SDK for basic syntax check
IOS_SDK=$(xcrun --show-sdk-path --sdk iphoneos 2>/dev/null)
if [ -n "$IOS_SDK" ]; then
for swift_file in $SWIFT_FILES; do
# Quick syntax check without full compilation
if ! swiftc -sdk "$IOS_SDK" -target arm64-apple-ios16.0 -parse "$swift_file" 2>&1 | grep -q "error:"; then
continue
else
log_warn "Syntax check found issues in $swift_file (will be caught during build)"
# Don't exit - let xcodebuild catch real errors
fi
done
else
log_warn "Could not find iOS SDK, skipping syntax validation"
fi
fi
cd ..
# Clean build
log_step "Cleaning iOS build..."
xcodebuild clean \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-quiet || {
log_warn "Clean failed or unnecessary, continuing..."
}
# Check if iOS device platform is available
log_step "Checking iOS device platform availability..."
BUILD_DEVICE=false
if xcodebuild -showsdks 2>&1 | grep -q "iOS.*iphoneos"; then
IOS_SDK_VERSION=$(xcrun --show-sdk-version --sdk iphoneos 2>&1)
log_info "Found iOS SDK: $IOS_SDK_VERSION"
# Check if platform components are installed by trying a list command
# Note: -dry-run is not supported in new build system, so we check SDK availability differently
if xcodebuild -showsdks 2>&1 | grep -q "iphoneos"; then
# Try to validate SDK path exists
SDK_PATH=$(xcrun --show-sdk-path --sdk iphoneos 2>&1)
if [ $? -eq 0 ] && [ -d "$SDK_PATH" ]; then
# Check if we can actually build (by trying to list build settings)
LIST_OUTPUT=$(xcodebuild -workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-destination 'generic/platform=iOS' \
-showBuildSettings 2>&1 | head -5)
if echo "$LIST_OUTPUT" | grep -q "iOS.*is not installed"; then
log_warn "iOS device platform components not installed"
log_info "To install iOS device platform components, run:"
log_info " xcodebuild -downloadPlatform iOS"
log_info "Or via Xcode: Settings > Components > iOS $IOS_SDK_VERSION"
log_info ""
log_info "Building for iOS Simulator instead (sufficient for plugin development)"
else
BUILD_DEVICE=true
fi
else
log_warn "iOS SDK path not accessible: $SDK_PATH"
log_info "Building for iOS Simulator instead"
fi
else
log_warn "iOS device SDK not found in xcodebuild -showsdks"
log_info "Building for iOS Simulator instead"
fi
else
log_warn "iOS SDK not found"
fi
# Build for device (iOS) if available
if [ "$BUILD_DEVICE" = true ]; then
log_step "Building for iOS device (arm64)..."
BUILD_OUTPUT=$(xcodebuild build \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-derivedDataPath build/derivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1)
BUILD_EXIT_CODE=$?
if echo "$BUILD_OUTPUT" | grep -q "error.*iOS.*is not installed"; then
log_warn "iOS device build failed - platform components not installed"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
log_info "Check build log: /tmp/xcodebuild_device.log"
BUILD_DEVICE=false
elif echo "$BUILD_OUTPUT" | grep -q "BUILD FAILED"; then
log_warn "iOS device build failed"
log_info ""
log_info "=== DEVICE BUILD ERRORS ==="
echo "$BUILD_OUTPUT" | grep -E "(error:|warning:|BUILD FAILED)"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
log_info ""
log_info "Full build log saved to: /tmp/xcodebuild_device.log"
log_info "View full log: cat /tmp/xcodebuild_device.log"
log_info "Falling back to simulator build..."
BUILD_DEVICE=false
elif echo "$BUILD_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
log_info "✓ iOS device build completed"
elif [ $BUILD_EXIT_CODE -ne 0 ]; then
log_warn "iOS device build failed (exit code: $BUILD_EXIT_CODE)"
log_info ""
log_info "=== DEVICE BUILD ERRORS ==="
echo "$BUILD_OUTPUT" | grep -E "(error:|warning:|BUILD FAILED)"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
log_info ""
log_info "Full build log saved to: /tmp/xcodebuild_device.log"
log_info "View full log: cat /tmp/xcodebuild_device.log"
log_info "Falling back to simulator build..."
BUILD_DEVICE=false
else
log_warn "iOS device build completed with warnings"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
fi
fi
# Build for simulator
log_step "Building for iOS simulator..."
SIMULATOR_BUILD_OUTPUT=$(xcodebuild build \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build/derivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1)
SIMULATOR_EXIT_CODE=$?
# Save full output to log file
echo "$SIMULATOR_BUILD_OUTPUT" > /tmp/xcodebuild_simulator.log
if echo "$SIMULATOR_BUILD_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
log_info "✓ iOS simulator build completed successfully"
elif echo "$SIMULATOR_BUILD_OUTPUT" | grep -q "error:"; then
log_error "iOS simulator build failed"
log_info ""
log_info "Full error output:"
echo "$SIMULATOR_BUILD_OUTPUT" | grep -E "(error:|warning:)"
log_info ""
log_info "Full build log saved to: /tmp/xcodebuild_simulator.log"
log_info "View full log: cat /tmp/xcodebuild_simulator.log"
log_info "View errors only: grep -E '(error:|warning:)' /tmp/xcodebuild_simulator.log"
exit 1
elif [ $SIMULATOR_EXIT_CODE -ne 0 ]; then
log_error "iOS simulator build failed (exit code: $SIMULATOR_EXIT_CODE)"
log_info ""
log_info "Build output (last 50 lines):"
echo "$SIMULATOR_BUILD_OUTPUT" | tail -50
log_info ""
log_info "Full build log saved to: /tmp/xcodebuild_simulator.log"
log_info "View full log: cat /tmp/xcodebuild_simulator.log"
exit 1
else
log_warn "iOS simulator build completed with warnings"
echo "$SIMULATOR_BUILD_OUTPUT" | grep -E "(warning:|error:)" | head -10
fi
# Find built frameworks
DEVICE_FRAMEWORK=$(find build/derivedData -path "*/Build/Products/*-iphoneos/DailyNotificationPlugin.framework" -type d | head -1)
SIMULATOR_FRAMEWORK=$(find build/derivedData -path "*/Build/Products/*-iphonesimulator/DailyNotificationPlugin.framework" -type d | head -1)
if [ -n "$DEVICE_FRAMEWORK" ]; then
log_info "✓ Device framework: $DEVICE_FRAMEWORK"
fi
if [ -n "$SIMULATOR_FRAMEWORK" ]; then
log_info "✓ Simulator framework: $SIMULATOR_FRAMEWORK"
fi
# Create universal framework (optional)
if [ -n "$DEVICE_FRAMEWORK" ] && [ -n "$SIMULATOR_FRAMEWORK" ]; then
log_step "Creating universal framework..."
UNIVERSAL_DIR="build/universal"
mkdir -p "$UNIVERSAL_DIR"
# Copy device framework
cp -R "$DEVICE_FRAMEWORK" "$UNIVERSAL_DIR/"
# Create universal binary
UNIVERSAL_FRAMEWORK="$UNIVERSAL_DIR/DailyNotificationPlugin.framework/DailyNotificationPlugin"
if lipo -create \
"$DEVICE_FRAMEWORK/DailyNotificationPlugin" \
"$SIMULATOR_FRAMEWORK/DailyNotificationPlugin" \
-output "$UNIVERSAL_FRAMEWORK" 2>/dev/null; then
log_info "✓ Universal framework: $UNIVERSAL_DIR/DailyNotificationPlugin.framework"
else
log_warn "Universal framework creation failed (may not be needed)"
fi
fi
cd ..
log_info "iOS build completed successfully!"
}
# Main build process
main() {
log_info "Starting build process..."
# Parse command line arguments
BUILD_PLATFORM="all"
while [[ $# -gt 0 ]]; do
case $1 in
--platform)
BUILD_PLATFORM="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [--platform PLATFORM]"
echo ""
echo "Options:"
echo " --platform PLATFORM Build platform: 'android', 'ios', or 'all' (default: all)"
echo ""
echo "Examples:"
echo " $0 --platform android # Build Android only"
echo " $0 --platform ios # Build iOS only"
echo " $0 --platform all # Build both platforms"
echo " $0 # Build both platforms (default)"
exit 0
;;
*)
log_error "Unknown option: $1"
log_error "Use --help for usage information"
exit 1
;;
esac
done
# Check environment (platform-specific)
check_environment "$BUILD_PLATFORM"
# Build TypeScript
build_typescript
# Build based on platform
case $BUILD_PLATFORM in
"android")
build_android
;;
"ios")
build_ios
;;
"all")
build_android
build_ios
;;
*)
log_error "Invalid platform: $BUILD_PLATFORM. Use 'android', 'ios', or 'all'"
exit 1
;;
esac
log_info "Build completed successfully!"
}
# Run main function with all arguments
main "$@"