Trois des étapes du runbook sont des fichiers texte, donc faisables hors Xcode : * `CoachQuickWidget()` enregistré dans `CoachLiveActivityBundle`, sous `if #available(iOS 17.0, *)` — le bundle sert aussi des cibles antérieures ; * schéma `coachapp://` déclaré dans l'Info.plist de l'App. Il n'y était pas : seul le schéma Google était présent, et les boutons Photo et Scanner du widget n'auraient donc rien ouvert ; * `CoachQuickSync.flush()` appelé depuis `applicationDidBecomeActive`, qui était un corps vide. Ne restent en manuel que les gestes qui touchent le projet Xcode lui-même : Target Membership des trois fichiers Swift, puis le build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
75 lines
3.2 KiB
Swift
75 lines
3.2 KiB
Swift
import UIKit
|
|
import Capacitor
|
|
#if canImport(GoogleSignIn)
|
|
import GoogleSignIn
|
|
#endif
|
|
|
|
@UIApplicationMain
|
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
|
|
var window: UIWindow?
|
|
|
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
injectCoachAuthCookie()
|
|
return true
|
|
}
|
|
|
|
/// Injecte le cookie d'auth coach.hypnotruck.ch dans HTTPCookieStorage.shared
|
|
/// avant que la WKWebView ne charge la 1ère page. Évite de passer par /login
|
|
/// (le redirect 303 casse la WKWebView avec WebKitErrorDomain code 102).
|
|
///
|
|
/// Le token est lu depuis la constante `kCoachWebToken` définie dans
|
|
/// `CoachAuth.swift` (gitignored). Si le fichier est absent, l'app
|
|
/// fonctionne mais demande le token au 1er lancement via /login.
|
|
private func injectCoachAuthCookie() {
|
|
let token = CoachAuth.kCoachWebToken
|
|
guard !token.isEmpty, token != "REPLACE_ME" else { return }
|
|
|
|
// N'écrase PAS une session existante (ex. login natif Apple = cookie
|
|
// de session per-user déjà posé). On n'injecte le token bypass que si
|
|
// aucun cookie coach_web_token n'est présent (1er lancement).
|
|
if let cookies = HTTPCookieStorage.shared.cookies,
|
|
cookies.contains(where: { $0.name == "coach_web_token" && $0.domain.contains("coach.hypnotruck.ch") }) {
|
|
return
|
|
}
|
|
|
|
let oneYear: TimeInterval = 60 * 60 * 24 * 365
|
|
let properties: [HTTPCookiePropertyKey: Any] = [
|
|
.name: "coach_web_token",
|
|
.value: token,
|
|
.domain: "coach.hypnotruck.ch",
|
|
.path: "/",
|
|
.secure: true,
|
|
.expires: Date(timeIntervalSinceNow: oneYear),
|
|
]
|
|
|
|
guard let cookie = HTTPCookie(properties: properties) else { return }
|
|
HTTPCookieStorage.shared.setCookie(cookie)
|
|
}
|
|
|
|
func applicationWillResignActive(_ application: UIApplication) {}
|
|
func applicationDidEnterBackground(_ application: UIApplication) {}
|
|
func applicationWillEnterForeground(_ application: UIApplication) {}
|
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
|
// Les saisies faites depuis le widget (eau, café) attendent dans l'App
|
|
// Group : une extension n'a ni session ni droit de tenir une requête.
|
|
// On les transmet ici, à chaque retour au premier plan.
|
|
Task { await CoachQuickSync.flush() }
|
|
}
|
|
func applicationWillTerminate(_ application: UIApplication) {}
|
|
|
|
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
#if canImport(GoogleSignIn)
|
|
// Retour du flux OAuth Google (reversed client id URL scheme).
|
|
if GIDSignIn.sharedInstance.handle(url) {
|
|
return true
|
|
}
|
|
#endif
|
|
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
|
|
}
|
|
|
|
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
|
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
|
}
|
|
}
|