Init Coach Hypnotruck iOS — Capacitor 8 wrapper
- Capacitor 8 + plugins natifs (push, local-notif, geoloc, HealthKit Capgo, splash, status bar, haptics) - Config pointe vers https://coach.hypnotruck.ch (server.url) - Icône 1024×1024 + splash 2732×2732 depuis icon-512 PWA coach_sportif - README complet avec procédure build sur Mac mini - Bundle ID : ch.hypnotruck.coach, App name : Coach Hypnotruck
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
node_modules/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# iOS / Xcode
|
||||||
|
ios/App/Pods/
|
||||||
|
ios/App/build/
|
||||||
|
ios/App/App.xcworkspace/xcuserdata/
|
||||||
|
ios/App/App.xcodeproj/xcuserdata/
|
||||||
|
ios/DerivedData/
|
||||||
|
ios/capacitor-cordova-ios-plugins/
|
||||||
|
*.xcuserstate
|
||||||
|
|
||||||
|
# Capacitor
|
||||||
|
ios/App/public/
|
||||||
|
.capacitor/
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.mobileprovision
|
||||||
|
*.cer
|
||||||
|
ExportOptions.plist
|
||||||
154
README.md
Normal file
154
README.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Coach Hypnotruck — iOS
|
||||||
|
|
||||||
|
Wrapper natif iOS de [coach.hypnotruck.ch](https://coach.hypnotruck.ch) basé sur Capacitor 8.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Backend** : FastAPI + Jinja2 hébergé sur le VPS, accessible via `https://coach.hypnotruck.ch`
|
||||||
|
- **Coque iOS** : Capacitor 8 charge l'URL distante dans un WKWebView
|
||||||
|
- **Plugins natifs** : push notifications, local notifications, géolocalisation, HealthKit (Capgo), splash, status bar, haptics
|
||||||
|
|
||||||
|
Le code web n'est pas embarqué — l'app pointe vers la prod via `server.url` dans `capacitor.config.ts`. Avantage : déploiement web = mise à jour instantanée de l'app sans review Apple.
|
||||||
|
|
||||||
|
## Bundle ID & nom
|
||||||
|
|
||||||
|
- **Bundle ID** : `ch.hypnotruck.coach`
|
||||||
|
- **App Store name** : `Coach Hypnotruck`
|
||||||
|
- **Display name** : `Coach Hypnotruck`
|
||||||
|
|
||||||
|
## Prérequis (sur le Mac mini)
|
||||||
|
|
||||||
|
- macOS 13+ (Ventura ou plus récent)
|
||||||
|
- Xcode 15+ (App Store, ~10 Go)
|
||||||
|
- Command Line Tools : `xcode-select --install`
|
||||||
|
- Homebrew + `brew install node cocoapods`
|
||||||
|
- Node 20 LTS (compatible avec Cap 8)
|
||||||
|
- Compte Apple Developer Program actif (99 USD/an)
|
||||||
|
- Apple ID `dev@hypnotruck.ch` connecté dans Xcode → Settings → Accounts
|
||||||
|
|
||||||
|
## Workflow build
|
||||||
|
|
||||||
|
### 1. Cloner le repo sur le Mac mini
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@gitea:sylvain/coach-ios.git
|
||||||
|
cd coach-ios
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Générer les icônes & splash
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @capacitor/assets generate --ios
|
||||||
|
```
|
||||||
|
|
||||||
|
Génère toutes les tailles requises depuis `resources/icon.png` (1024×1024) et `resources/splash.png` (2732×2732).
|
||||||
|
|
||||||
|
### 3. Ajouter la plateforme iOS (à faire UNE SEULE FOIS sur le Mac)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx cap add ios
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette commande crée le dossier `ios/` avec le projet Xcode, qui sera ensuite committé.
|
||||||
|
|
||||||
|
### 4. Sync (à chaque changement de plugins ou config)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx cap sync ios
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Ouvrir dans Xcode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx cap open ios
|
||||||
|
```
|
||||||
|
|
||||||
|
Dans Xcode :
|
||||||
|
|
||||||
|
1. Sélectionner le projet **App** dans la navigation gauche
|
||||||
|
2. Onglet **Signing & Capabilities** :
|
||||||
|
- Team : ton équipe Apple Developer
|
||||||
|
- Bundle Identifier : `ch.hypnotruck.coach`
|
||||||
|
- Cocher "Automatically manage signing"
|
||||||
|
3. Ajouter les capabilities requises :
|
||||||
|
- **HealthKit** (lecture FC, pas, calories, VO2max)
|
||||||
|
- **Push Notifications**
|
||||||
|
- **Background Modes** → Remote notifications
|
||||||
|
4. Onglet **Info** : ajouter les clés Privacy obligatoires (voir section ci-dessous)
|
||||||
|
|
||||||
|
### 6. Build sur simulateur / device
|
||||||
|
|
||||||
|
- Simulateur : choisir un iPhone dans la barre du haut → ⌘R
|
||||||
|
- Device physique : connecter iPhone par USB, sélectionner, ⌘R (la 1ère fois iOS demande de faire confiance au certificat dev dans Réglages → Général → VPN et gestion des appareils)
|
||||||
|
|
||||||
|
### 7. Upload TestFlight
|
||||||
|
|
||||||
|
1. Xcode → Product → **Archive**
|
||||||
|
2. Fenêtre Organizer s'ouvre → sélectionner l'archive → **Distribute App** → **App Store Connect** → **Upload**
|
||||||
|
3. Sur [App Store Connect](https://appstoreconnect.apple.com) : créer une nouvelle app (1ère fois), section **TestFlight** → ajouter testeurs (jusqu'à 100 internes sans review, 10 000 externes avec review légère)
|
||||||
|
|
||||||
|
## Privacy keys requises (Info.plist)
|
||||||
|
|
||||||
|
Sans ces clés, iOS crashe à l'usage du plugin. À ajouter dans Xcode → onglet Info → Custom iOS Target Properties :
|
||||||
|
|
||||||
|
| Clé | Valeur (description affichée à l'utilisateur) |
|
||||||
|
|---|---|
|
||||||
|
| `NSHealthShareUsageDescription` | "Coach Hypnotruck lit vos données de santé (FC, pas, calories) pour adapter vos séances." |
|
||||||
|
| `NSHealthUpdateUsageDescription` | "Coach Hypnotruck enregistre vos séances dans Santé pour suivre votre progression." |
|
||||||
|
| `NSLocationWhenInUseUsageDescription` | "Coach Hypnotruck utilise votre position pour tracer vos sorties course et VTT." |
|
||||||
|
| `NSLocationAlwaysAndWhenInUseUsageDescription` | "Permet le suivi GPS pendant les sorties même app en arrière-plan." |
|
||||||
|
| `NSMotionUsageDescription` | "Coach Hypnotruck utilise les capteurs de mouvement pour mesurer votre activité." |
|
||||||
|
| `NSCameraUsageDescription` | "Coach Hypnotruck peut utiliser l'appareil photo pour scanner des codes-barres alimentaires." |
|
||||||
|
|
||||||
|
## Anti-rejet App Store (Guideline 4.2)
|
||||||
|
|
||||||
|
Apple rejette les apps qui sont juste un wrapper webview sans valeur native. Pour passer review :
|
||||||
|
|
||||||
|
- ✅ **Push notifications natives** (rappel séances) — implémenté côté iOS via `@capacitor/push-notifications`
|
||||||
|
- ✅ **HealthKit** (lecture FC, pas, calories) — implémenté via `@capgo/capacitor-health`
|
||||||
|
- ✅ **Géolocalisation** native pour tracking sorties
|
||||||
|
- ✅ **Local notifications** (rappels hors-ligne)
|
||||||
|
- ✅ **Haptics** sur actions importantes
|
||||||
|
- ⏳ À implémenter côté web : appeler ces plugins via JS bridge depuis coach.hypnotruck.ch
|
||||||
|
|
||||||
|
## Structure repo
|
||||||
|
|
||||||
|
```
|
||||||
|
coach-ios/
|
||||||
|
├── capacitor.config.ts # Config Capacitor (bundle ID, URL, plugins)
|
||||||
|
├── package.json # Deps Capacitor + plugins
|
||||||
|
├── www/ # Page de bootstrap (chargement, fallback)
|
||||||
|
│ └── index.html
|
||||||
|
├── resources/ # Source icônes & splash (1024×1024, 2732×2732)
|
||||||
|
│ ├── icon.png
|
||||||
|
│ └── splash.png
|
||||||
|
├── ios/ # Projet Xcode (généré par `cap add ios` sur Mac)
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mise à jour de l'app
|
||||||
|
|
||||||
|
| Type de modif | Action requise |
|
||||||
|
|---|---|
|
||||||
|
| Changement web (templates, CSS, routes) | **Aucune** — l'app charge la prod en direct |
|
||||||
|
| Ajout plugin Capacitor | `npm install` + `npx cap sync ios` + nouveau build TestFlight |
|
||||||
|
| Changement icône / splash | `npx @capacitor/assets generate --ios` + sync + nouveau build |
|
||||||
|
| Changement bundle ID, version, capabilities | Build + soumission Apple review |
|
||||||
|
|
||||||
|
## TODO Phase 0
|
||||||
|
|
||||||
|
- [ ] Sur le Mac : `npx cap add ios` et commit du dossier `ios/`
|
||||||
|
- [ ] Configurer signing dans Xcode avec Apple ID `dev@hypnotruck.ch`
|
||||||
|
- [ ] Ajouter les Privacy keys dans Info.plist
|
||||||
|
- [ ] 1er build simulateur pour vérifier que la PWA charge bien
|
||||||
|
- [ ] 1er build device physique (iPhone perso)
|
||||||
|
- [ ] Upload TestFlight pour beta interne
|
||||||
|
- [ ] Préparer screenshots App Store (6.7" iPhone, min 3)
|
||||||
|
- [ ] Privacy policy URL (obligatoire avec HealthKit) — à héberger sur coach.hypnotruck.ch/privacy
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- [Doc Capacitor](https://capacitorjs.com/docs)
|
||||||
|
- [Capgo Health plugin](https://github.com/Cap-go/capacitor-health)
|
||||||
|
- [Apple Developer](https://developer.apple.com/account)
|
||||||
45
capacitor.config.ts
Normal file
45
capacitor.config.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type { CapacitorConfig } from '@capacitor/cli';
|
||||||
|
|
||||||
|
const config: CapacitorConfig = {
|
||||||
|
appId: 'ch.hypnotruck.coach',
|
||||||
|
appName: 'Coach Hypnotruck',
|
||||||
|
webDir: 'www',
|
||||||
|
|
||||||
|
server: {
|
||||||
|
url: 'https://coach.hypnotruck.ch',
|
||||||
|
cleartext: false,
|
||||||
|
androidScheme: 'https',
|
||||||
|
},
|
||||||
|
|
||||||
|
ios: {
|
||||||
|
contentInset: 'always',
|
||||||
|
scrollEnabled: true,
|
||||||
|
limitsNavigationsToAppBoundDomains: false,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
SplashScreen: {
|
||||||
|
launchShowDuration: 1500,
|
||||||
|
launchAutoHide: true,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
showSpinner: false,
|
||||||
|
splashImmersive: false,
|
||||||
|
},
|
||||||
|
StatusBar: {
|
||||||
|
style: 'DEFAULT',
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
overlaysWebView: false,
|
||||||
|
},
|
||||||
|
PushNotifications: {
|
||||||
|
presentationOptions: ['badge', 'sound', 'alert'],
|
||||||
|
},
|
||||||
|
LocalNotifications: {
|
||||||
|
smallIcon: 'ic_stat_icon_config_sample',
|
||||||
|
iconColor: '#488AFF',
|
||||||
|
sound: 'beep.wav',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
5312
package-lock.json
generated
Normal file
5312
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "coach-ios",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Coach Hypnotruck — wrapper iOS Capacitor de coach.hypnotruck.ch",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"sync": "cap sync ios",
|
||||||
|
"open:ios": "cap open ios",
|
||||||
|
"build:ios": "cap sync ios && cap open ios"
|
||||||
|
},
|
||||||
|
"author": "Sylvain Bettinelli",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"dependencies": {
|
||||||
|
"@capacitor/app": "^8.1.0",
|
||||||
|
"@capacitor/core": "^8.3.1",
|
||||||
|
"@capacitor/geolocation": "^8.2.0",
|
||||||
|
"@capacitor/haptics": "^8.0.2",
|
||||||
|
"@capacitor/ios": "^8.3.1",
|
||||||
|
"@capacitor/local-notifications": "^8.0.2",
|
||||||
|
"@capacitor/push-notifications": "^8.0.3",
|
||||||
|
"@capacitor/splash-screen": "^8.0.1",
|
||||||
|
"@capacitor/status-bar": "^8.0.2",
|
||||||
|
"@capgo/capacitor-health": "^8.4.9"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@capacitor/assets": "^3.0.5",
|
||||||
|
"@capacitor/cli": "^8.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
resources/icon.png
Normal file
BIN
resources/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
resources/splash.png
Normal file
BIN
resources/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
52
www/index.html
Normal file
52
www/index.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
|
<meta name="format-detection" content="telephone=no" />
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: https://ssl.gstatic.com" />
|
||||||
|
<title>Coach Hypnotruck</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #111;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wrap { padding: 24px; }
|
||||||
|
h1 { font-size: 22px; margin: 0 0 8px; }
|
||||||
|
p { font-size: 15px; color: #555; margin: 0; }
|
||||||
|
.spinner {
|
||||||
|
margin: 24px auto 0;
|
||||||
|
width: 36px; height: 36px;
|
||||||
|
border: 3px solid #eee;
|
||||||
|
border-top-color: #2b7cff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.9s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>Coach Hypnotruck</h1>
|
||||||
|
<p>Chargement…</p>
|
||||||
|
<div class="spinner"></div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// Fallback: si le WebView ne redirige pas via server.url, on force le chargement.
|
||||||
|
// En conditions normales, Capacitor charge directement https://coach.hypnotruck.ch
|
||||||
|
setTimeout(function () {
|
||||||
|
if (location.hostname !== 'coach.hypnotruck.ch') {
|
||||||
|
location.href = 'https://coach.hypnotruck.ch';
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user