fix(test-app): iOS permission handling and build improvements
- Add BGTask identifiers and background modes to iOS Info.plist - Fix permission method calls (checkPermissionStatus vs checkPermissions) - Implement Android-style permission checking pattern - Add "Request Permissions" action card with check-then-request flow - Fix simulator selection in build script (use device ID for reliability) - Add Podfile auto-fix to fix-capacitor-plugins.js - Update build documentation with unified script usage Fixes: - BGTask registration errors (Info.plist missing identifiers) - Permission method not found errors (checkPermissions -> checkPermissionStatus) - Simulator selection failures (now uses device ID) - Podfile incorrect pod name (TimesafariDailyNotificationPlugin -> DailyNotificationPlugin) The permission flow now matches Android: check status first, then show system dialog if needed. iOS system dialog appears automatically when requestNotificationPermissions() is called. Files changed: - test-apps/daily-notification-test/ios/App/App/Info.plist (new) - test-apps/daily-notification-test/src/lib/typed-plugin.ts - test-apps/daily-notification-test/src/views/HomeView.vue - test-apps/daily-notification-test/scripts/build.sh (new) - test-apps/daily-notification-test/scripts/fix-capacitor-plugins.js - test-apps/daily-notification-test/docs/BUILD_QUICK_REFERENCE.md - test-apps/daily-notification-test/README.md - test-apps/daily-notification-test/package.json - test-apps/daily-notification-test/package-lock.json
This commit is contained in:
@@ -31,20 +31,57 @@ npm install
|
||||
|
||||
**Note**: The `postinstall` script automatically fixes Capacitor configuration files after installation.
|
||||
|
||||
### Capacitor Sync (Android)
|
||||
## Building for Android and iOS
|
||||
|
||||
### Quick Build (Recommended)
|
||||
|
||||
Use the unified build script for both platforms:
|
||||
|
||||
```bash
|
||||
# Build and run both platforms on emulator/simulator
|
||||
./scripts/build.sh --run
|
||||
|
||||
# Build both platforms (no run)
|
||||
./scripts/build.sh
|
||||
|
||||
# Build Android only
|
||||
./scripts/build.sh --android
|
||||
|
||||
# Build iOS only
|
||||
./scripts/build.sh --ios
|
||||
|
||||
# Build and run Android on emulator
|
||||
./scripts/build.sh --run-android
|
||||
|
||||
# Build and run iOS on simulator
|
||||
./scripts/build.sh --run-ios
|
||||
```
|
||||
|
||||
**See**: `docs/BUILD_QUICK_REFERENCE.md` for detailed build instructions.
|
||||
|
||||
### Manual Build Steps
|
||||
|
||||
#### Capacitor Sync
|
||||
|
||||
**Important**: Use the wrapper script instead of `npx cap sync` directly to automatically fix plugin paths:
|
||||
|
||||
```sh
|
||||
# Sync both platforms
|
||||
npm run cap:sync
|
||||
|
||||
# Sync Android only
|
||||
npm run cap:sync:android
|
||||
|
||||
# Sync iOS only
|
||||
npm run cap:sync:ios
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Run `npx cap sync android`
|
||||
1. Run `npx cap sync` (or platform-specific sync)
|
||||
2. Automatically fix `capacitor.settings.gradle` (corrects plugin path from `android/` to `android/plugin/`)
|
||||
3. Ensure `capacitor.plugins.json` has the correct plugin registration
|
||||
|
||||
If you run `npx cap sync android` directly, you can manually fix afterward:
|
||||
If you run `npx cap sync` directly, you can manually fix afterward:
|
||||
```sh
|
||||
node scripts/fix-capacitor-plugins.js
|
||||
```
|
||||
|
||||
@@ -1,48 +1,195 @@
|
||||
# Build Process Quick Reference
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: October 17, 2025
|
||||
**Date**: October 17, 2025
|
||||
**Last Updated**: November 19, 2025
|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
**Easiest way to build and run:**
|
||||
|
||||
```bash
|
||||
# Build and run both platforms
|
||||
./scripts/build.sh --run
|
||||
|
||||
# Or build only
|
||||
./scripts/build.sh
|
||||
```
|
||||
|
||||
This script handles everything automatically! See [Unified Build Script](#-unified-build-script-recommended) section for all options.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Build Steps
|
||||
|
||||
### Prerequisites
|
||||
|
||||
**Required for All Platforms:**
|
||||
- Node.js 20.19.0+ or 22.12.0+
|
||||
- npm (comes with Node.js)
|
||||
- Plugin must be built (`npm run build` in plugin root directory)
|
||||
- The build script will automatically build the plugin if `dist/` doesn't exist
|
||||
|
||||
**For Android:**
|
||||
- Java JDK 22.12 or later
|
||||
- Android SDK (with `adb` in PATH)
|
||||
- Gradle (comes with Android project)
|
||||
|
||||
**For iOS:**
|
||||
- macOS with Xcode (xcodebuild must be in PATH)
|
||||
- CocoaPods (`pod --version` to check)
|
||||
- Can be installed via rbenv (script handles this automatically)
|
||||
- iOS deployment target: iOS 13.0+
|
||||
|
||||
**Plugin Requirements:**
|
||||
- Plugin podspec must exist at: `node_modules/@timesafari/daily-notification-plugin/ios/DailyNotificationPlugin.podspec`
|
||||
- Plugin must be installed: `npm install` (uses `file:../../` reference)
|
||||
- Plugin must be built: `npm run build` in plugin root (creates `dist/` directory)
|
||||
|
||||
### Initial Setup (One-Time)
|
||||
|
||||
```bash
|
||||
# 1. Build web assets
|
||||
# 1. Install dependencies (includes @capacitor/ios)
|
||||
npm install
|
||||
|
||||
# 2. Add iOS platform (if not already added)
|
||||
npx cap add ios
|
||||
|
||||
# 3. Install iOS dependencies
|
||||
cd ios/App
|
||||
pod install
|
||||
cd ../..
|
||||
```
|
||||
|
||||
### Build for Both Platforms
|
||||
|
||||
```bash
|
||||
# 1. Build web assets (required for both platforms)
|
||||
npm run build
|
||||
|
||||
# 2. Sync with native projects (automatically fixes plugin paths)
|
||||
# 2. Sync with native projects (syncs both Android and iOS)
|
||||
npm run cap:sync
|
||||
|
||||
# 3. Build and deploy Android
|
||||
# OR sync platforms individually:
|
||||
# npm run cap:sync:android # Android only
|
||||
# npm run cap:sync:ios # iOS only
|
||||
```
|
||||
|
||||
### Android Build
|
||||
|
||||
```bash
|
||||
# Build Android APK
|
||||
cd android
|
||||
./gradlew :app:assembleDebug
|
||||
|
||||
# Install on device/emulator
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
# Launch app
|
||||
adb shell am start -n com.timesafari.dailynotification.test/.MainActivity
|
||||
```
|
||||
|
||||
### iOS Build
|
||||
|
||||
```bash
|
||||
# Open in Xcode
|
||||
cd ios/App
|
||||
open App.xcworkspace
|
||||
|
||||
# Or build from command line
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme App \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
build
|
||||
|
||||
# Or use Capacitor CLI
|
||||
npx cap run ios
|
||||
```
|
||||
|
||||
## ⚠️ Why `npm run cap:sync` is Important
|
||||
|
||||
**Problem**: `npx cap sync` overwrites `capacitor.plugins.json` and `capacitor.settings.gradle` with incorrect paths.
|
||||
|
||||
**Solution**: The `cap:sync` script automatically:
|
||||
1. Runs `npx cap sync android`
|
||||
1. Runs `npx cap sync` (syncs both Android and iOS)
|
||||
2. Fixes `capacitor.settings.gradle` (corrects plugin path from `android/` to `android/plugin/`)
|
||||
3. Restores the DailyNotification plugin entry in `capacitor.plugins.json`
|
||||
|
||||
**Platform-specific sync:**
|
||||
- `npm run cap:sync:android` - Syncs Android only (includes fix script)
|
||||
- `npm run cap:sync:ios` - Syncs iOS only (no fix needed for iOS)
|
||||
|
||||
**Without the fix**: Plugin detection fails, build errors occur, "simplified dialog" appears.
|
||||
|
||||
## 🔍 Verification Checklist
|
||||
|
||||
After build, verify:
|
||||
### Android Verification
|
||||
|
||||
- [ ] `capacitor.plugins.json` contains DailyNotification entry
|
||||
After Android build, verify:
|
||||
|
||||
- [ ] `android/app/src/main/assets/capacitor.plugins.json` contains DailyNotification entry
|
||||
- [ ] System Status shows "Plugin: Available" (green)
|
||||
- [ ] Plugin Diagnostics shows all 4 plugins
|
||||
- [ ] Click events work on ActionCard components
|
||||
- [ ] No "simplified dialog" appears
|
||||
|
||||
## 🛠️ Automated Build Script
|
||||
### iOS Verification
|
||||
|
||||
Create `scripts/build-and-deploy.sh`:
|
||||
After iOS build, verify:
|
||||
|
||||
- [ ] iOS project exists at `ios/App/App.xcworkspace`
|
||||
- [ ] CocoaPods installed (`pod install` completed successfully)
|
||||
- [ ] Plugin framework linked in Xcode project
|
||||
- [ ] App builds without errors in Xcode
|
||||
- [ ] Plugin methods accessible from JavaScript
|
||||
- [ ] System Status shows "Plugin: Available" (green)
|
||||
|
||||
## 🛠️ Automated Build Scripts
|
||||
|
||||
### Unified Build Script (Recommended)
|
||||
|
||||
The project includes a comprehensive build script that handles both platforms:
|
||||
|
||||
```bash
|
||||
# Build both platforms
|
||||
./scripts/build.sh
|
||||
|
||||
# Build Android only
|
||||
./scripts/build.sh --android
|
||||
|
||||
# Build iOS only
|
||||
./scripts/build.sh --ios
|
||||
|
||||
# Build and run Android on emulator
|
||||
./scripts/build.sh --run-android
|
||||
|
||||
# Build and run iOS on simulator
|
||||
./scripts/build.sh --run-ios
|
||||
|
||||
# Build and run both platforms
|
||||
./scripts/build.sh --run
|
||||
|
||||
# Show help
|
||||
./scripts/build.sh --help
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- ✅ Automatically builds web assets
|
||||
- ✅ Syncs Capacitor with both platforms
|
||||
- ✅ Builds Android APK
|
||||
- ✅ Builds iOS app for simulator
|
||||
- ✅ Automatically finds and uses available emulator/simulator
|
||||
- ✅ Installs and launches apps when using `--run` flags
|
||||
- ✅ Color-coded output for easy reading
|
||||
- ✅ Comprehensive error handling
|
||||
|
||||
### Manual Build Scripts
|
||||
|
||||
#### Build for Android
|
||||
|
||||
Create `scripts/build-android.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
@@ -52,7 +199,7 @@ echo "🔨 Building web assets..."
|
||||
npm run build
|
||||
|
||||
echo "🔄 Syncing with native projects..."
|
||||
npm run cap:sync
|
||||
npm run cap:sync:android
|
||||
# This automatically syncs and fixes plugin paths
|
||||
|
||||
echo "🏗️ Building Android app..."
|
||||
@@ -63,7 +210,64 @@ echo "📱 Installing and launching..."
|
||||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||
adb shell am start -n com.timesafari.dailynotification.test/.MainActivity
|
||||
|
||||
echo "✅ Build and deploy complete!"
|
||||
echo "✅ Android build and deploy complete!"
|
||||
```
|
||||
|
||||
### Build for iOS
|
||||
|
||||
Create `scripts/build-ios.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🔨 Building web assets..."
|
||||
npm run build
|
||||
|
||||
echo "🔄 Syncing with iOS..."
|
||||
npm run cap:sync:ios
|
||||
|
||||
echo "🍎 Building iOS app..."
|
||||
cd ios/App
|
||||
pod install
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme App \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
build
|
||||
|
||||
echo "✅ iOS build complete!"
|
||||
echo "📱 Open Xcode to run on simulator: open App.xcworkspace"
|
||||
```
|
||||
|
||||
### Build for Both Platforms
|
||||
|
||||
Create `scripts/build-all.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🔨 Building web assets..."
|
||||
npm run build
|
||||
|
||||
echo "🔄 Syncing with all native projects..."
|
||||
npm run cap:sync
|
||||
|
||||
echo "🏗️ Building Android..."
|
||||
cd android && ./gradlew :app:assembleDebug && cd ..
|
||||
|
||||
echo "🍎 Building iOS..."
|
||||
cd ios/App && pod install && cd ../..
|
||||
xcodebuild -workspace ios/App/App.xcworkspace \
|
||||
-scheme App \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
build
|
||||
|
||||
echo "✅ All platforms built successfully!"
|
||||
```
|
||||
|
||||
## 🐛 Common Issues
|
||||
@@ -74,9 +278,15 @@ echo "✅ Build and deploy complete!"
|
||||
| Plugin not detected | "Plugin: Not Available" (red) | Check plugin registry, rebuild |
|
||||
| Click events not working | Buttons don't respond | Check Vue 3 compatibility, router config |
|
||||
| Inconsistent status | Different status in different cards | Use consistent detection logic |
|
||||
| **No podspec found** | `[!] No podspec found for 'TimesafariDailyNotificationPlugin'` | Run `node scripts/fix-capacitor-plugins.js` to fix Podfile, then `pod install` |
|
||||
| **Plugin not built** | Vite build fails: "Failed to resolve entry" | Run `npm run build` in plugin root directory (`../../`) |
|
||||
| **CocoaPods not found** | `pod: command not found` | Install CocoaPods: `sudo gem install cocoapods` or via rbenv |
|
||||
| **Xcode not found** | `xcodebuild: command not found` | Install Xcode from App Store, run `xcode-select --install` |
|
||||
|
||||
## 📱 Testing Commands
|
||||
|
||||
### Android Testing
|
||||
|
||||
```bash
|
||||
# Check plugin registry
|
||||
cat android/app/src/main/assets/capacitor.plugins.json
|
||||
@@ -86,8 +296,38 @@ adb logcat | grep -i "dailynotification\|capacitor\|plugin"
|
||||
|
||||
# Check app installation
|
||||
adb shell pm list packages | grep dailynotification
|
||||
|
||||
# View app logs
|
||||
adb logcat -s DailyNotification
|
||||
```
|
||||
|
||||
### iOS Testing
|
||||
|
||||
```bash
|
||||
# Check if iOS project exists
|
||||
ls -la ios/App/App.xcworkspace
|
||||
|
||||
# Check CocoaPods installation
|
||||
cd ios/App && pod install && cd ../..
|
||||
|
||||
# Monitor iOS logs (simulator)
|
||||
xcrun simctl spawn booted log stream | grep -i "dailynotification\|capacitor\|plugin"
|
||||
|
||||
# Check plugin in Xcode
|
||||
# Open ios/App/App.xcworkspace in Xcode
|
||||
# Check: Project Navigator → Frameworks → DailyNotificationPlugin.framework
|
||||
|
||||
# View device logs (physical device)
|
||||
# Xcode → Window → Devices and Simulators → Select device → Open Console
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Use `npm run cap:sync` instead of `npx cap sync android` directly - it automatically fixes the configuration files!
|
||||
## 📝 Important Notes
|
||||
|
||||
**Remember**:
|
||||
- Use `npm run cap:sync` to sync both platforms (automatically fixes Android configuration files)
|
||||
- Use `npm run cap:sync:android` for Android-only sync (includes fix script)
|
||||
- Use `npm run cap:sync:ios` for iOS-only sync
|
||||
- Always run `npm run build` before syncing to ensure latest web assets are copied
|
||||
- For iOS: Run `pod install` in `ios/App/` after first sync or when dependencies change
|
||||
|
||||
62
test-apps/daily-notification-test/ios/App/App/Info.plist
Normal file
62
test-apps/daily-notification-test/ios/App/App/Info.plist
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Daily Notification Test</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.timesafari.dailynotification.fetch</string>
|
||||
<string>com.timesafari.dailynotification.notify</string>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>background-fetch</string>
|
||||
<string>background-processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>NSUserNotificationsUsageDescription</key>
|
||||
<string>This app uses notifications to deliver daily updates and reminders.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"../..": {
|
||||
"name": "@timesafari/daily-notification-plugin",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.11",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build",
|
||||
"lint": "eslint . --fix",
|
||||
"cap:sync": "npx cap sync android && node scripts/fix-capacitor-plugins.js",
|
||||
"cap:sync": "npx cap sync && node scripts/fix-capacitor-plugins.js",
|
||||
"cap:sync:android": "npx cap sync android && node scripts/fix-capacitor-plugins.js",
|
||||
"cap:sync:ios": "npx cap sync ios",
|
||||
"postinstall": "node scripts/fix-capacitor-plugins.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor/android": "^6.2.1",
|
||||
"@capacitor/ios": "^6.2.1",
|
||||
"@capacitor/cli": "^6.2.1",
|
||||
"@capacitor/core": "^6.2.1",
|
||||
"@timesafari/daily-notification-plugin": "file:../../",
|
||||
|
||||
569
test-apps/daily-notification-test/scripts/build.sh
Executable file
569
test-apps/daily-notification-test/scripts/build.sh
Executable file
@@ -0,0 +1,569 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build script for daily-notification-test Capacitor app
|
||||
# Supports both Android and iOS with emulator/simulator deployment
|
||||
#
|
||||
# Requirements:
|
||||
# - Node.js 20.19.0+ or 22.12.0+
|
||||
# - npm
|
||||
# - Plugin must be built (script will auto-build if needed)
|
||||
# - For Android: Java JDK 22.12+, Android SDK (adb)
|
||||
# - For iOS: Xcode, CocoaPods (pod)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build.sh # Build both platforms
|
||||
# ./scripts/build.sh --android # Build Android only
|
||||
# ./scripts/build.sh --ios # Build iOS only
|
||||
# ./scripts/build.sh --run # Build and run both
|
||||
# ./scripts/build.sh --help # Show help
|
||||
|
||||
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
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
# Get pod command (handles rbenv)
|
||||
get_pod_command() {
|
||||
if command -v pod &> /dev/null; then
|
||||
echo "pod"
|
||||
elif [ -f "$HOME/.rbenv/shims/pod" ]; then
|
||||
echo "$HOME/.rbenv/shims/pod"
|
||||
else
|
||||
log_error "CocoaPods (pod) not found. Please install CocoaPods first."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check requirements
|
||||
check_requirements() {
|
||||
log_step "Checking build requirements..."
|
||||
|
||||
local missing_requirements=false
|
||||
|
||||
# Check Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
log_error "Node.js is not installed. Please install Node.js 20.19.0+ or 22.12.0+"
|
||||
missing_requirements=true
|
||||
else
|
||||
log_info "✅ Node.js: $(node --version)"
|
||||
fi
|
||||
|
||||
# Check npm
|
||||
if ! command -v npm &> /dev/null; then
|
||||
log_error "npm is not installed"
|
||||
missing_requirements=true
|
||||
else
|
||||
log_info "✅ npm: $(npm --version)"
|
||||
fi
|
||||
|
||||
# Check plugin is built
|
||||
PLUGIN_ROOT="$(cd "$PROJECT_DIR/../.." && pwd)"
|
||||
if [ ! -d "$PLUGIN_ROOT/dist" ]; then
|
||||
log_warn "Plugin not built. Building plugin now..."
|
||||
cd "$PLUGIN_ROOT"
|
||||
if npm run build; then
|
||||
log_info "✅ Plugin built successfully"
|
||||
else
|
||||
log_error "Failed to build plugin. Please run 'npm run build' in the plugin root directory."
|
||||
missing_requirements=true
|
||||
fi
|
||||
cd "$PROJECT_DIR"
|
||||
else
|
||||
log_info "✅ Plugin built (dist/ exists)"
|
||||
fi
|
||||
|
||||
# Check Android requirements if building Android
|
||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
||||
if ! command -v adb &> /dev/null; then
|
||||
log_warn "Android SDK not found (adb not in PATH). Android build will be skipped."
|
||||
else
|
||||
log_info "✅ Android SDK: $(adb version | head -1)"
|
||||
fi
|
||||
|
||||
if ! command -v java &> /dev/null; then
|
||||
log_warn "Java not found. Android build may fail."
|
||||
else
|
||||
log_info "✅ Java: $(java -version 2>&1 | head -1)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check iOS requirements if building iOS
|
||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_warn "Xcode not found (xcodebuild not in PATH). iOS build will be skipped."
|
||||
else
|
||||
log_info "✅ Xcode: $(xcodebuild -version | head -1)"
|
||||
fi
|
||||
|
||||
POD_CMD=$(get_pod_command 2>/dev/null || echo "")
|
||||
if [ -z "$POD_CMD" ]; then
|
||||
log_warn "CocoaPods not found. iOS build will be skipped."
|
||||
else
|
||||
log_info "✅ CocoaPods: $($POD_CMD --version 2>/dev/null || echo 'found')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$missing_requirements" = true ]; then
|
||||
log_error "Missing required dependencies. Please install them and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "All requirements satisfied"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
BUILD_ANDROID=false
|
||||
BUILD_IOS=false
|
||||
BUILD_ALL=true
|
||||
RUN_ANDROID=false
|
||||
RUN_IOS=false
|
||||
RUN_ALL=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--android)
|
||||
BUILD_ANDROID=true
|
||||
BUILD_ALL=false
|
||||
shift
|
||||
;;
|
||||
--ios)
|
||||
BUILD_IOS=true
|
||||
BUILD_ALL=false
|
||||
shift
|
||||
;;
|
||||
--run-android)
|
||||
RUN_ANDROID=true
|
||||
BUILD_ANDROID=true
|
||||
BUILD_ALL=false
|
||||
shift
|
||||
;;
|
||||
--run-ios)
|
||||
RUN_IOS=true
|
||||
BUILD_IOS=true
|
||||
BUILD_ALL=false
|
||||
shift
|
||||
;;
|
||||
--run)
|
||||
RUN_ALL=true
|
||||
BUILD_ALL=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --android Build Android only"
|
||||
echo " --ios Build iOS only"
|
||||
echo " --run-android Build and run Android on emulator"
|
||||
echo " --run-ios Build and run iOS on simulator"
|
||||
echo " --run Build and run both platforms"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "Default: Build both platforms (no run)"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Change to project directory
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
log_info "Building daily-notification-test app"
|
||||
log_info "Project directory: $PROJECT_DIR"
|
||||
|
||||
# Check requirements
|
||||
check_requirements
|
||||
|
||||
# Step 1: Build web assets
|
||||
log_step "Building web assets..."
|
||||
if ! npm run build; then
|
||||
log_error "Web build failed"
|
||||
exit 1
|
||||
fi
|
||||
log_info "Web assets built successfully"
|
||||
|
||||
# Step 2: Sync Capacitor
|
||||
log_step "Syncing Capacitor with native projects..."
|
||||
if ! npm run cap:sync; then
|
||||
log_error "Capacitor sync failed"
|
||||
exit 1
|
||||
fi
|
||||
log_info "Capacitor sync completed"
|
||||
|
||||
# Step 2.5: Ensure fix script ran (it should have via cap:sync, but verify for iOS)
|
||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
||||
if [ -d "$PROJECT_DIR/ios" ]; then
|
||||
log_step "Verifying iOS Podfile configuration..."
|
||||
if node "$PROJECT_DIR/scripts/fix-capacitor-plugins.js" 2>/dev/null; then
|
||||
log_info "iOS Podfile verified"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Android build
|
||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_ANDROID" = true ]; then
|
||||
log_step "Building Android app..."
|
||||
|
||||
# Check for Android SDK
|
||||
if ! command -v adb &> /dev/null; then
|
||||
log_warn "adb not found. Android SDK may not be installed."
|
||||
log_warn "Skipping Android build. Install Android SDK to build Android."
|
||||
else
|
||||
cd "$PROJECT_DIR/android"
|
||||
|
||||
# Build APK
|
||||
if ./gradlew :app:assembleDebug; then
|
||||
log_info "Android APK built successfully"
|
||||
|
||||
APK_PATH="$PROJECT_DIR/android/app/build/outputs/apk/debug/app-debug.apk"
|
||||
|
||||
if [ -f "$APK_PATH" ]; then
|
||||
log_info "APK location: $APK_PATH"
|
||||
|
||||
# Run on emulator if requested
|
||||
if [ "$RUN_ALL" = true ] || [ "$RUN_ANDROID" = true ]; then
|
||||
log_step "Installing and launching Android app..."
|
||||
|
||||
# Check for running emulator
|
||||
if ! adb devices | grep -q "device$"; then
|
||||
log_warn "No Android emulator/device found"
|
||||
log_info "Please start an Android emulator and try again"
|
||||
log_info "Or use: adb devices to check connected devices"
|
||||
else
|
||||
# Install APK
|
||||
if adb install -r "$APK_PATH"; then
|
||||
log_info "APK installed successfully"
|
||||
|
||||
# Launch app
|
||||
if adb shell am start -n com.timesafari.dailynotification.test/.MainActivity; then
|
||||
log_info "✅ Android app launched successfully!"
|
||||
else
|
||||
log_warn "Failed to launch app (may already be running)"
|
||||
fi
|
||||
else
|
||||
log_warn "APK installation failed (may already be installed)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_error "APK not found at expected location: $APK_PATH"
|
||||
fi
|
||||
else
|
||||
log_error "Android build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
# iOS build
|
||||
if [ "$BUILD_ALL" = true ] || [ "$BUILD_IOS" = true ]; then
|
||||
log_step "Building iOS app..."
|
||||
|
||||
# Check for Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_warn "xcodebuild not found. Xcode may not be installed."
|
||||
log_warn "Skipping iOS build. Install Xcode to build iOS."
|
||||
else
|
||||
IOS_DIR="$PROJECT_DIR/ios/App"
|
||||
|
||||
if [ ! -d "$IOS_DIR" ]; then
|
||||
log_warn "iOS directory not found. Adding iOS platform..."
|
||||
cd "$PROJECT_DIR"
|
||||
npx cap add ios
|
||||
fi
|
||||
|
||||
cd "$IOS_DIR"
|
||||
|
||||
# Install CocoaPods dependencies
|
||||
log_step "Installing CocoaPods dependencies..."
|
||||
POD_CMD=$(get_pod_command)
|
||||
|
||||
# Check if Podfile exists and has correct plugin reference
|
||||
if [ -f "$IOS_DIR/Podfile" ]; then
|
||||
# Run fix script to ensure Podfile is correct
|
||||
log_step "Verifying Podfile configuration..."
|
||||
if node "$PROJECT_DIR/scripts/fix-capacitor-plugins.js" 2>/dev/null; then
|
||||
log_info "Podfile verified"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $POD_CMD install; then
|
||||
log_info "CocoaPods dependencies installed"
|
||||
else
|
||||
log_error "CocoaPods install failed"
|
||||
log_info "Troubleshooting:"
|
||||
log_info "1. Check that plugin podspec exists: ls -la $PLUGIN_ROOT/ios/DailyNotificationPlugin.podspec"
|
||||
log_info "2. Verify Podfile references: pod 'DailyNotificationPlugin', :path => '../../node_modules/@timesafari/daily-notification-plugin/ios'"
|
||||
log_info "3. Run fix script: node scripts/fix-capacitor-plugins.js"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find workspace
|
||||
WORKSPACE="$IOS_DIR/App.xcworkspace"
|
||||
if [ ! -d "$WORKSPACE" ]; then
|
||||
WORKSPACE="$IOS_DIR/App.xcodeproj"
|
||||
fi
|
||||
|
||||
if [ ! -d "$WORKSPACE" ]; then
|
||||
log_error "Xcode workspace/project not found at $IOS_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get simulator
|
||||
log_step "Finding available iOS simulator..."
|
||||
|
||||
# Method 1: Use xcodebuild to get available destinations (most reliable)
|
||||
# This gives us the exact format xcodebuild expects
|
||||
DESTINATION_STRING=$(xcodebuild -workspace "$WORKSPACE" -scheme App -showdestinations 2>/dev/null | \
|
||||
grep "iOS Simulator" | \
|
||||
grep -i "iphone" | \
|
||||
grep -v "iPhone Air" | \
|
||||
head -1)
|
||||
|
||||
if [ -n "$DESTINATION_STRING" ]; then
|
||||
# Extract name from destination string
|
||||
# Format: "platform=iOS Simulator,id=...,name=iPhone 17 Pro,OS=26.0.1"
|
||||
SIMULATOR=$(echo "$DESTINATION_STRING" | \
|
||||
sed -n 's/.*name=\([^,]*\).*/\1/p' | \
|
||||
sed 's/[[:space:]]*$//')
|
||||
log_info "Found simulator via xcodebuild: $SIMULATOR"
|
||||
fi
|
||||
|
||||
# Method 2: Fallback to simctl if xcodebuild didn't work
|
||||
if [ -z "$SIMULATOR" ] || [ "$SIMULATOR" = "Shutdown" ] || [ "$SIMULATOR" = "Booted" ]; then
|
||||
# Use simctl list in JSON format for more reliable parsing
|
||||
# This avoids parsing status words like "Shutdown"
|
||||
SIMULATOR_JSON=$(xcrun simctl list devices available --json 2>/dev/null)
|
||||
|
||||
if [ -n "$SIMULATOR_JSON" ]; then
|
||||
# Extract first iPhone device name using jq if available, or grep/sed
|
||||
if command -v jq &> /dev/null; then
|
||||
SIMULATOR=$(echo "$SIMULATOR_JSON" | \
|
||||
jq -r '.devices | to_entries[] | .value[] | select(.name | test("iPhone"; "i")) | .name' | \
|
||||
grep -v "iPhone Air" | \
|
||||
head -1)
|
||||
else
|
||||
# Fallback: parse text output more carefully
|
||||
# Get line with iPhone, extract name before first parenthesis
|
||||
SIMULATOR_LINE=$(xcrun simctl list devices available 2>/dev/null | \
|
||||
grep -E "iPhone [0-9]" | \
|
||||
grep -v "iPhone Air" | \
|
||||
head -1)
|
||||
|
||||
if [ -n "$SIMULATOR_LINE" ]; then
|
||||
# Extract device name - everything before first "("
|
||||
SIMULATOR=$(echo "$SIMULATOR_LINE" | \
|
||||
sed -E 's/^[[:space:]]*([^(]+).*/\1/' | \
|
||||
sed 's/[[:space:]]*$//')
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate it's not a status word
|
||||
if [ "$SIMULATOR" = "Shutdown" ] || [ "$SIMULATOR" = "Booted" ] || [ "$SIMULATOR" = "Creating" ] || [ -z "$SIMULATOR" ]; then
|
||||
SIMULATOR=""
|
||||
else
|
||||
log_info "Found simulator via simctl: $SIMULATOR"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 3: Try to find iPhone 17 Pro specifically (preferred)
|
||||
if [ -z "$SIMULATOR" ] || [ "$SIMULATOR" = "Shutdown" ] || [ "$SIMULATOR" = "Booted" ]; then
|
||||
PRO_LINE=$(xcrun simctl list devices available 2>/dev/null | \
|
||||
grep -i "iPhone 17 Pro" | \
|
||||
head -1)
|
||||
|
||||
if [ -n "$PRO_LINE" ]; then
|
||||
PRO_SIM=$(echo "$PRO_LINE" | \
|
||||
awk -F'(' '{print $1}' | \
|
||||
sed 's/^[[:space:]]*//' | \
|
||||
sed 's/[[:space:]]*$//')
|
||||
|
||||
if [ -n "$PRO_SIM" ] && [ "$PRO_SIM" != "Shutdown" ] && [ "$PRO_SIM" != "Booted" ] && [ "$PRO_SIM" != "Creating" ]; then
|
||||
SIMULATOR="$PRO_SIM"
|
||||
log_info "Using preferred simulator: $SIMULATOR"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final fallback to known good simulator
|
||||
if [ -z "$SIMULATOR" ] || [ "$SIMULATOR" = "Shutdown" ] || [ "$SIMULATOR" = "Booted" ] || [ "$SIMULATOR" = "Creating" ]; then
|
||||
# Try common simulator names that are likely to exist
|
||||
for DEFAULT_SIM in "iPhone 17 Pro" "iPhone 17" "iPhone 16" "iPhone 15 Pro" "iPhone 15"; do
|
||||
if xcrun simctl list devices available 2>/dev/null | grep -q "$DEFAULT_SIM"; then
|
||||
SIMULATOR="$DEFAULT_SIM"
|
||||
log_info "Using fallback simulator: $SIMULATOR"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If still empty, use iPhone 17 Pro as final default
|
||||
if [ -z "$SIMULATOR" ] || [ "$SIMULATOR" = "Shutdown" ] || [ "$SIMULATOR" = "Booted" ]; then
|
||||
log_warn "Could not determine simulator. Using default: iPhone 17 Pro"
|
||||
SIMULATOR="iPhone 17 Pro"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Selected simulator: $SIMULATOR"
|
||||
|
||||
# Extract device ID for more reliable targeting
|
||||
# Format: " iPhone 17 Pro (68D19D08-4701-422C-AF61-2E21ACA1DD4C) (Shutdown)"
|
||||
SIMULATOR_ID=$(xcrun simctl list devices available 2>/dev/null | \
|
||||
grep -i "$SIMULATOR" | \
|
||||
head -1 | \
|
||||
sed -n 's/.*(\([A-F0-9-]\{36\}\)).*/\1/p')
|
||||
|
||||
# Verify simulator exists before building
|
||||
if [ -z "$SIMULATOR_ID" ] && ! xcrun simctl list devices available 2>/dev/null | grep -q "$SIMULATOR"; then
|
||||
log_warn "Simulator '$SIMULATOR' not found in available devices"
|
||||
log_info "Available iPhone simulators:"
|
||||
xcrun simctl list devices available 2>/dev/null | grep -i "iphone" | grep -v "iPhone Air" | head -5
|
||||
log_warn "Attempting build anyway with: $SIMULATOR"
|
||||
fi
|
||||
|
||||
# Build iOS app
|
||||
log_step "Building iOS app for simulator..."
|
||||
|
||||
# Use device ID if available, otherwise use name
|
||||
if [ -n "$SIMULATOR_ID" ]; then
|
||||
DESTINATION="platform=iOS Simulator,id=$SIMULATOR_ID"
|
||||
log_info "Using simulator ID: $SIMULATOR_ID ($SIMULATOR)"
|
||||
else
|
||||
DESTINATION="platform=iOS Simulator,name=$SIMULATOR"
|
||||
log_info "Using simulator name: $SIMULATOR"
|
||||
fi
|
||||
|
||||
if xcodebuild -workspace "$WORKSPACE" \
|
||||
-scheme App \
|
||||
-configuration Debug \
|
||||
-sdk iphonesimulator \
|
||||
-destination "$DESTINATION" \
|
||||
build; then
|
||||
log_info "iOS app built successfully"
|
||||
|
||||
# Find built app
|
||||
DERIVED_DATA="$HOME/Library/Developer/Xcode/DerivedData"
|
||||
APP_PATH=$(find "$DERIVED_DATA" -name "App.app" -path "*/Build/Products/Debug-iphonesimulator/*" -type d 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$APP_PATH" ]; then
|
||||
log_info "App built at: $APP_PATH"
|
||||
|
||||
# Run on simulator if requested
|
||||
if [ "$RUN_ALL" = true ] || [ "$RUN_IOS" = true ]; then
|
||||
log_step "Installing and launching iOS app on simulator..."
|
||||
|
||||
# Use the device ID we already extracted, or get it again
|
||||
if [ -z "$SIMULATOR_ID" ]; then
|
||||
SIMULATOR_ID=$(xcrun simctl list devices available 2>/dev/null | \
|
||||
grep -i "$SIMULATOR" | \
|
||||
head -1 | \
|
||||
sed -n 's/.*(\([A-F0-9-]\{36\}\)).*/\1/p')
|
||||
fi
|
||||
|
||||
# If we have device ID, use it; otherwise try to boot by name
|
||||
if [ -n "$SIMULATOR_ID" ]; then
|
||||
SIMULATOR_UDID="$SIMULATOR_ID"
|
||||
log_info "Using simulator ID: $SIMULATOR_UDID"
|
||||
else
|
||||
# Try to boot simulator by name and get its ID
|
||||
log_step "Booting simulator: $SIMULATOR..."
|
||||
xcrun simctl boot "$SIMULATOR" 2>/dev/null || true
|
||||
sleep 2
|
||||
SIMULATOR_UDID=$(xcrun simctl list devices 2>/dev/null | \
|
||||
grep -i "$SIMULATOR" | \
|
||||
grep -E "\([A-F0-9-]{36}\)" | \
|
||||
head -1 | \
|
||||
sed -n 's/.*(\([A-F0-9-]\{36\}\)).*/\1/p')
|
||||
fi
|
||||
|
||||
if [ -n "$SIMULATOR_UDID" ]; then
|
||||
# Install app
|
||||
if xcrun simctl install "$SIMULATOR_UDID" "$APP_PATH"; then
|
||||
log_info "App installed on simulator"
|
||||
|
||||
# Launch app
|
||||
APP_BUNDLE_ID="com.timesafari.dailynotification.test"
|
||||
if xcrun simctl launch "$SIMULATOR_UDID" "$APP_BUNDLE_ID"; then
|
||||
log_info "✅ iOS app launched successfully!"
|
||||
else
|
||||
log_warn "Failed to launch app (may already be running)"
|
||||
fi
|
||||
else
|
||||
log_warn "App installation failed (may already be installed)"
|
||||
fi
|
||||
else
|
||||
log_warn "Could not find or boot simulator"
|
||||
log_info "Open Xcode and run manually: open $WORKSPACE"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warn "Could not find built app in DerivedData"
|
||||
log_info "Build succeeded. Open Xcode to run: open $WORKSPACE"
|
||||
fi
|
||||
else
|
||||
log_error "iOS build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info ""
|
||||
log_info "✅ Build process complete!"
|
||||
log_info ""
|
||||
|
||||
# Summary
|
||||
if [ "$BUILD_ANDROID" = true ] || [ "$BUILD_ALL" = true ]; then
|
||||
if [ -f "$PROJECT_DIR/android/app/build/outputs/apk/debug/app-debug.apk" ]; then
|
||||
log_info "Android APK: $PROJECT_DIR/android/app/build/outputs/apk/debug/app-debug.apk"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$BUILD_IOS" = true ] || [ "$BUILD_ALL" = true ]; then
|
||||
if [ -d "$IOS_DIR/App.xcworkspace" ]; then
|
||||
log_info "iOS Workspace: $IOS_DIR/App.xcworkspace"
|
||||
log_info "Open with: open $IOS_DIR/App.xcworkspace"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -22,6 +22,7 @@ const __dirname = path.dirname(__filename);
|
||||
|
||||
const PLUGINS_JSON_PATH = path.join(__dirname, '../android/app/src/main/assets/capacitor.plugins.json');
|
||||
const SETTINGS_GRADLE_PATH = path.join(__dirname, '../android/capacitor.settings.gradle');
|
||||
const PODFILE_PATH = path.join(__dirname, '../ios/App/Podfile');
|
||||
|
||||
const PLUGIN_ENTRY = {
|
||||
name: "DailyNotification",
|
||||
@@ -103,6 +104,98 @@ ${correctPath}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix iOS Podfile to use correct plugin pod name and path
|
||||
*/
|
||||
function fixPodfile() {
|
||||
console.log('🔧 Verifying iOS Podfile...');
|
||||
|
||||
if (!fs.existsSync(PODFILE_PATH)) {
|
||||
console.log('ℹ️ Podfile not found (iOS platform may not be added yet)');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let content = fs.readFileSync(PODFILE_PATH, 'utf8');
|
||||
const originalContent = content;
|
||||
|
||||
// The correct pod reference should be:
|
||||
// pod 'DailyNotificationPlugin', :path => '../../node_modules/@timesafari/daily-notification-plugin/ios'
|
||||
const correctPodLine = "pod 'DailyNotificationPlugin', :path => '../../node_modules/@timesafari/daily-notification-plugin/ios'";
|
||||
|
||||
// Check if Podfile already has the correct reference
|
||||
if (content.includes("pod 'DailyNotificationPlugin'")) {
|
||||
// Check if path is correct
|
||||
if (content.includes('@timesafari/daily-notification-plugin/ios')) {
|
||||
console.log('✅ Podfile has correct DailyNotificationPlugin reference');
|
||||
} else {
|
||||
// Fix the path
|
||||
console.log('⚠️ Podfile has DailyNotificationPlugin but wrong path - fixing...');
|
||||
content = content.replace(
|
||||
/pod ['"]DailyNotificationPlugin['"].*:path.*/,
|
||||
correctPodLine
|
||||
);
|
||||
|
||||
// Also fix if it's using the wrong name (TimesafariDailyNotificationPlugin)
|
||||
content = content.replace(
|
||||
/pod ['"]TimesafariDailyNotificationPlugin['"].*:path.*/,
|
||||
correctPodLine
|
||||
);
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(PODFILE_PATH, content);
|
||||
console.log('✅ Fixed DailyNotificationPlugin path in Podfile');
|
||||
}
|
||||
}
|
||||
} else if (content.includes("TimesafariDailyNotificationPlugin")) {
|
||||
// Fix wrong pod name
|
||||
console.log('⚠️ Podfile uses wrong pod name (TimesafariDailyNotificationPlugin) - fixing...');
|
||||
content = content.replace(
|
||||
/pod ['"]TimesafariDailyNotificationPlugin['"].*:path.*/,
|
||||
correctPodLine
|
||||
);
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(PODFILE_PATH, content);
|
||||
console.log('✅ Fixed pod name in Podfile (TimesafariDailyNotificationPlugin -> DailyNotificationPlugin)');
|
||||
}
|
||||
} else {
|
||||
// Add the pod reference if it's missing
|
||||
console.log('⚠️ Podfile missing DailyNotificationPlugin - adding...');
|
||||
|
||||
// Find the capacitor_pods function or target section
|
||||
if (content.includes('def capacitor_pods')) {
|
||||
// Add after capacitor_pods function
|
||||
content = content.replace(
|
||||
/(def capacitor_pods[\s\S]*?end)/,
|
||||
`$1\n\n # Daily Notification Plugin\n ${correctPodLine}`
|
||||
);
|
||||
} else if (content.includes("target 'App'")) {
|
||||
// Add in target section
|
||||
content = content.replace(
|
||||
/(target 'App' do)/,
|
||||
`$1\n ${correctPodLine}`
|
||||
);
|
||||
} else {
|
||||
// Add at end before post_install
|
||||
content = content.replace(
|
||||
/(post_install)/,
|
||||
`${correctPodLine}\n\n$1`
|
||||
);
|
||||
}
|
||||
|
||||
if (content !== originalContent) {
|
||||
fs.writeFileSync(PODFILE_PATH, content);
|
||||
console.log('✅ Added DailyNotificationPlugin to Podfile');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error fixing Podfile:', error.message);
|
||||
// Don't exit - iOS might not be set up yet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all fixes
|
||||
*/
|
||||
@@ -112,9 +205,10 @@ function fixAll() {
|
||||
|
||||
fixCapacitorPlugins();
|
||||
fixCapacitorSettingsGradle();
|
||||
fixPodfile();
|
||||
|
||||
console.log('\n✅ All fixes applied successfully!');
|
||||
console.log('💡 These fixes will persist until the next "npx cap sync android"');
|
||||
console.log('💡 These fixes will persist until the next "npx cap sync"');
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
@@ -122,4 +216,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
fixAll();
|
||||
}
|
||||
|
||||
export { fixCapacitorPlugins, fixCapacitorSettingsGradle, fixAll };
|
||||
export { fixCapacitorPlugins, fixCapacitorSettingsGradle, fixPodfile, fixAll };
|
||||
|
||||
@@ -72,15 +72,20 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
|
||||
|
||||
/**
|
||||
* Check permissions with validation
|
||||
* Uses checkPermissionStatus() which is the correct method name for iOS
|
||||
*/
|
||||
async checkPermissions(): Promise<PermissionStatus> {
|
||||
try {
|
||||
const result = await (this.plugin as { checkPermissions: () => Promise<PermissionStatus> }).checkPermissions()
|
||||
// Use checkPermissionStatus() which is implemented on both iOS and Android
|
||||
const result = await (this.plugin as { checkPermissionStatus: () => Promise<any> }).checkPermissionStatus()
|
||||
|
||||
// Ensure response has required fields
|
||||
// Map PermissionStatusResult to PermissionStatus format
|
||||
return {
|
||||
notifications: result.notifications || 'denied',
|
||||
notificationsEnabled: Boolean(result.notificationsEnabled)
|
||||
notifications: result.notificationsEnabled ? 'granted' : 'denied',
|
||||
notificationsEnabled: Boolean(result.notificationsEnabled),
|
||||
exactAlarmEnabled: Boolean(result.exactAlarmEnabled),
|
||||
wakeLockEnabled: Boolean(result.wakeLockEnabled),
|
||||
allPermissionsGranted: Boolean(result.allPermissionsGranted)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -166,6 +171,26 @@ export class TypedDailyNotificationPlugin implements DailyNotificationBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request notification permissions (iOS method name)
|
||||
* This is an alias for requestPermissions() for iOS compatibility
|
||||
*/
|
||||
async requestNotificationPermissions(): Promise<void> {
|
||||
try {
|
||||
// Try requestNotificationPermissions first (iOS), fallback to requestPermissions
|
||||
if (typeof (this.plugin as any).requestNotificationPermissions === 'function') {
|
||||
await (this.plugin as { requestNotificationPermissions: () => Promise<void> }).requestNotificationPermissions()
|
||||
} else if (typeof (this.plugin as any).requestPermissions === 'function') {
|
||||
await (this.plugin as { requestPermissions: () => Promise<PermissionStatus> }).requestPermissions()
|
||||
} else {
|
||||
throw new Error('Neither requestNotificationPermissions nor requestPermissions is available')
|
||||
}
|
||||
} catch (error) {
|
||||
logError(error, 'requestNotificationPermissions')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open exact alarm settings
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,13 @@
|
||||
@click="checkSystemStatus"
|
||||
:loading="isCheckingStatus"
|
||||
/>
|
||||
<ActionCard
|
||||
icon="🔐"
|
||||
title="Request Permissions"
|
||||
description="Check and request notification permissions"
|
||||
@click="checkAndRequestPermissions"
|
||||
:loading="isRequestingPermissions"
|
||||
/>
|
||||
<ActionCard
|
||||
icon="🔔"
|
||||
title="View Notifications"
|
||||
@@ -218,7 +225,8 @@ const checkSystemStatus = async (): Promise<void> => {
|
||||
console.log('✅ Plugin available, checking status...')
|
||||
try {
|
||||
const status = await plugin.getNotificationStatus()
|
||||
const permissions = await plugin.checkPermissions()
|
||||
// Use checkPermissionStatus() which is the correct method name for iOS
|
||||
const permissions = await plugin.checkPermissionStatus()
|
||||
const exactAlarmStatus = await plugin.getExactAlarmStatus()
|
||||
|
||||
console.log('📊 Plugin status object:', status)
|
||||
@@ -232,17 +240,17 @@ const checkSystemStatus = async (): Promise<void> => {
|
||||
|
||||
console.log('📊 Plugin permissions:', permissions)
|
||||
console.log('📊 Permissions details:')
|
||||
console.log(' - notifications:', permissions.notifications)
|
||||
console.log(' - notificationsEnabled:', (permissions as unknown as Record<string, unknown>).notificationsEnabled)
|
||||
console.log(' - exactAlarmEnabled:', (permissions as unknown as Record<string, unknown>).exactAlarmEnabled)
|
||||
console.log(' - wakeLockEnabled:', (permissions as unknown as Record<string, unknown>).wakeLockEnabled)
|
||||
console.log(' - allPermissionsGranted:', (permissions as unknown as Record<string, unknown>).allPermissionsGranted)
|
||||
console.log(' - notificationsEnabled:', permissions.notificationsEnabled)
|
||||
console.log(' - exactAlarmEnabled:', permissions.exactAlarmEnabled)
|
||||
console.log(' - wakeLockEnabled:', permissions.wakeLockEnabled)
|
||||
console.log(' - allPermissionsGranted:', permissions.allPermissionsGranted)
|
||||
console.log('📊 Exact alarm status:', exactAlarmStatus)
|
||||
|
||||
// Map plugin response to app store format
|
||||
// checkPermissionStatus() returns PermissionStatusResult with boolean flags
|
||||
const mappedStatus = {
|
||||
canScheduleNow: status.isEnabled ?? false,
|
||||
postNotificationsGranted: permissions.notifications === 'granted',
|
||||
postNotificationsGranted: permissions.notificationsEnabled ?? false,
|
||||
channelEnabled: true, // Default for now
|
||||
channelImportance: 3, // Default for now
|
||||
channelId: 'daily-notifications',
|
||||
@@ -351,6 +359,80 @@ const refreshSystemStatus = async (): Promise<void> => {
|
||||
await checkSystemStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions and request if needed (Android pattern)
|
||||
* 1. Check permission status first
|
||||
* 2. If not granted, show system dialog
|
||||
* 3. Refresh status after request
|
||||
*/
|
||||
const checkAndRequestPermissions = async (): Promise<void> => {
|
||||
console.log('🔐 CLICK: Check and Request Permissions')
|
||||
|
||||
if (isRequestingPermissions.value) {
|
||||
console.log('⏳ Permission request already in progress')
|
||||
return
|
||||
}
|
||||
|
||||
isRequestingPermissions.value = true
|
||||
|
||||
try {
|
||||
const { DailyNotification } = await import('@timesafari/daily-notification-plugin')
|
||||
const plugin = DailyNotification
|
||||
|
||||
if (!plugin) {
|
||||
console.error('❌ DailyNotification plugin not available')
|
||||
return
|
||||
}
|
||||
|
||||
// Step 1: Check permission status first (Android pattern)
|
||||
console.log('🔍 Step 1: Checking current permission status...')
|
||||
const permissionStatus = await plugin.checkPermissionStatus()
|
||||
|
||||
console.log('📊 Permission status:', {
|
||||
notificationsEnabled: permissionStatus.notificationsEnabled,
|
||||
exactAlarmEnabled: permissionStatus.exactAlarmEnabled,
|
||||
allPermissionsGranted: permissionStatus.allPermissionsGranted
|
||||
})
|
||||
|
||||
// Step 2: If not granted, show system dialog
|
||||
if (!permissionStatus.notificationsEnabled) {
|
||||
console.log('⚠️ Permissions not granted - showing system dialog...')
|
||||
console.log('📱 iOS will show native permission dialog now...')
|
||||
|
||||
// Request permissions - this will show the iOS system dialog
|
||||
// Try requestNotificationPermissions first (iOS), fallback to requestPermissions
|
||||
if (typeof (plugin as any).requestNotificationPermissions === 'function') {
|
||||
await (plugin as { requestNotificationPermissions: () => Promise<void> }).requestNotificationPermissions()
|
||||
} else if (typeof (plugin as any).requestPermissions === 'function') {
|
||||
await (plugin as { requestPermissions: () => Promise<any> }).requestPermissions()
|
||||
} else {
|
||||
throw new Error('Permission request method not available')
|
||||
}
|
||||
|
||||
console.log('✅ Permission request completed')
|
||||
|
||||
// Step 3: Refresh status after request
|
||||
console.log('🔄 Refreshing status after permission request...')
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)) // Wait 1 second for system to update
|
||||
await checkSystemStatus()
|
||||
} else {
|
||||
console.log('✅ Permissions already granted - no dialog needed')
|
||||
// Still refresh status to show current state
|
||||
await checkSystemStatus()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Permission check/request failed:', error)
|
||||
console.error('❌ Error details:', {
|
||||
name: (error as Error).name,
|
||||
message: (error as Error).message,
|
||||
stack: (error as Error).stack
|
||||
})
|
||||
} finally {
|
||||
isRequestingPermissions.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const runPluginDiagnostics = async (): Promise<void> => {
|
||||
console.log('🔄 CLICK: Plugin Diagnostics - METHOD CALLED!')
|
||||
console.log('🔄 FUNCTION START: runPluginDiagnostics called at', new Date().toISOString())
|
||||
|
||||
Reference in New Issue
Block a user