跳转至

This is the right strategic move. By focusing on a Barebones Demo (Proof of Concept) first, you validate the hardest technical link—the Watch-to-Phone-to-Cloud pipeline—before spending months on UI polish.

Here is the dual-track plan.


🛑 Part 1: The Barebones Feasibility Demo (The "Pipe Cleaner")

Goal: Prove that you can speak into the Watch, and text appears in a database. Timeline: 1 week (Sprint). Scope: No User Auth (Hardcoded User), No Timeline UI, No "Smart" Questions. Just Input -> Processing -> Output.

1.1 Architecture (The "Happy Path")

  1. Watch: Tap "Record" -> Speak -> Tap "Stop" -> File sends to Phone.
  2. Phone: Receives file -> Uploads to Firebase Storage -> Listens for database update.
  3. Cloud: Cloud Function triggers -> Gemini 2.0 Flash transcribes -> Writes to Firestore.
  4. Phone: UI updates with the text transcript.

1.2 Implementation Steps

Step A: The Watch App (SwiftUI)

Create a new Target in Xcode: Watch App. * UI: A single big Button that toggles a Bool isRecording. * Audio: Use AVAudioRecorder to save to a local temporary URL (.m4a). * Connectivity: Use WCSession.default.transferFile(url, metadata: nil).

Step B: The Phone Bridge (Flutter + Swift)

You need to catch the file. * Dependencies: flutter_watch_os_connectivity (easier) OR write a custom MethodChannel in AppDelegate.swift (cleaner). * Logic: 1. Phone receives file via session(_:didReceive:file:). 2. Phone moves file from "Inbox" to "Documents". 3. Phone triggers uploadToFirebase(file).

Step C: The Backend (Firebase)

  • Function: onObjectFinalized.
  • AI: Gemini 2.0 Flash.
  • Instruction: "Transcribe this audio. Return JSON: {text: '...'}."

Step D: The Verification UI

  • Flutter: A simple StreamBuilder listening to Firestore.collection('memories').
  • Success Criteria: You speak "Hello World" into your wrist, and 10 seconds later, "Hello World" appears on your iPhone screen.

🚀 Part 2: The Full Feature Deployable App (Production)

Goal: A polished, "Smart" autobiography agent. Timeline: 8–10 Weeks.

Phase 1: Foundation & Security (Weeks 1-2)

  • Auth: Implement firebase_auth (Sign in with Apple is mandatory for iOS apps).
  • Schema: Define User profile (DOB, Hometown) to ground the AI.
  • State Management: Use flutter_bloc or Riverpod to handle the complex state of uploading/processing.
  • Watch Polish: Add a "Complication" (Watch face widget) for one-tap access.

Phase 2: The "Brain" Integration (Weeks 3-5)

  • Vector Search: Implement the Embedding pipeline (converting summaries to vectors).
  • The "Slot Machine": Implement the random question generator logic in Cloud Functions.
  • Watch Notifications: Configure Push Notifications.
    • Flow: Cloud sends Push -> Watch receives -> User taps -> Opens Watch Recorder.

Phase 3: The Timeline & Visualization (Weeks 6-7)

  • Flutter UI: Build the vertical scrolling timeline.
  • Gap Analysis: Logic to detect missing years and highlight them visually.
  • Tags/Entities: UI to show "People mentioned" (extracted by Gemini).

Phase 4: Output & Monetization (Weeks 8-9)

  • Book Generation: The "Compile" button that sends 50 memories to Gemini 3.0 Pro to write a Chapter.
  • PDF Export: Rendering the text.
  • Subscription (Optional): Apple IAP (In-App Purchase) if you plan to charge for AI costs.

🛠️ Critical Implementation Guide (Code)

Here is the code for the most difficult part: Getting the Audio from the Watch to the Phone.

1. Apple Watch (Swift) - Sending the Audio

File: WatchApp/AudioRecorder.swift

import WatchConnectivity
import AVFoundation

class WatchSessionManager: NSObject, WCSessionDelegate {
    static let shared = WatchSessionManager()

    func startSession() {
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            WCSession.default.activate()
        }
    }

    // Required stub
    func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {}

    func sendAudio(fileURL: URL) {
        if WCSession.default.isReachable {
            // Transfers file in background, robust for large files
            WCSession.default.transferFile(fileURL, metadata: ["type": "audio"])
        }
    }
}

2. iPhone (Swift Side of Flutter) - Receiving the Audio

File: ios/Runner/AppDelegate.swift

You must intercept the file before Flutter even knows it exists.

import UIKit
import Flutter
import WatchConnectivity

@main
@objc class AppDelegate: FlutterAppDelegate, WCSessionDelegate {

    // Channel to talk to Flutter
    var flutterChannel: FlutterMethodChannel?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {

        // Setup WCSession
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate()
        }

        // Setup Flutter Channel
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        flutterChannel = FlutterMethodChannel(name: "com.autobio.watch", binaryMessenger: controller.binaryMessenger)

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    // MARK: - WCSession Delegate Methods

    // 1. Receive the File from Watch
    func session(_ session: WCSession, didReceive file: WCSessionFile) {
        DispatchQueue.main.async {
            do {
                // The file is in a temp folder. We MUST move it to Documents or it gets deleted.
                let fileManager = FileManager.default
                let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
                let destURL = documentsURL.appendingPathComponent(file.fileURL.lastPathComponent)

                // Overwrite if exists (for demo)
                try? fileManager.removeItem(at: destURL)
                try fileManager.moveItem(at: file.fileURL, to: destURL)

                // 2. Tell Flutter "Hey, I have a file at this path!"
                self.flutterChannel?.invokeMethod("didReceiveWatchFile", arguments: destURL.path)

            } catch {
                print("Error moving file: \(error)")
            }
        }
    }

    // Required Stubs
    func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {}
    func sessionDidBecomeInactive(_ session: WCSession) {}
    func sessionDidDeactivate(_ session: WCSession) {}
}

3. iPhone (Flutter Side) - Handling the Event

File: lib/services/watch_listener.dart

import 'package:flutter/services.dart';
import 'upload_service.dart'; // Your Firebase uploader

class WatchListener {
  static const platform = MethodChannel('com.autobio.watch');

  void initListener() {
    platform.setMethodCallHandler((call) async {
      if (call.method == "didReceiveWatchFile") {
        String filePath = call.arguments;
        print("Received file from Watch: $filePath");

        // TRIGGER UPLOAD IMMEDIATELY
        await UploadService.uploadFile(filePath);
      }
    });
  }
}

4. Cloud Function (The AI Processor)

File: functions/src/index.ts

// Standard Gemini 2.0 Flash Implementation
// This runs automatically when the file hits Storage
export const processWatchAudio = onObjectFinalized({ cpu: 2 }, async (event) => {
    const filePath = event.data.name;
    // ... (Your Gemini Code from previous prompts) ...
    // Save to Firestore
});

Summary of Feasibility Demo Logic

  1. Don't build a complex UI.
  2. Do implement the Swift code above. It solves the hardest problem: moving a file from a wrist device to a Flutter environment.
  3. Once this "Barebones" pipe works (Voice -> Watch -> Phone -> Cloud -> Text), building the rest of the app is just standard Flutter development.