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")¶
- Watch: Tap "Record" -> Speak -> Tap "Stop" -> File sends to Phone.
- Phone: Receives file -> Uploads to Firebase Storage -> Listens for database update.
- Cloud: Cloud Function triggers -> Gemini 2.0 Flash transcribes -> Writes to Firestore.
- 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
StreamBuilderlistening toFirestore.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
Userprofile (DOB, Hometown) to ground the AI. - State Management: Use
flutter_blocorRiverpodto 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¶
- Don't build a complex UI.
- Do implement the Swift code above. It solves the hardest problem: moving a file from a wrist device to a Flutter environment.
- Once this "Barebones" pipe works (Voice -> Watch -> Phone -> Cloud -> Text), building the rest of the app is just standard Flutter development.