diff --git a/apps/mobile-app/ios/build-and-submit.sh b/apps/mobile-app/ios/build-and-submit.sh
new file mode 100755
index 000000000..76bc7aff4
--- /dev/null
+++ b/apps/mobile-app/ios/build-and-submit.sh
@@ -0,0 +1,173 @@
+#!/usr/bin/env bash
+
+BUNDLE_ID="net.aliasvault.app"
+
+# Put the fastlane API key in the home directory
+API_KEY_PATH="$HOME/APPSTORE_CONNECT_FASTLANE.json"
+
+if [ ! -f "$API_KEY_PATH" ]; then
+ echo "❌ API key file '$API_KEY_PATH' does not exist. Please provide the App Store Connect API key at this path."
+ exit 1
+fi
+
+# ------------------------------------------
+# Shared function to extract version info
+# ------------------------------------------
+extract_version_info() {
+ local ipa_path="$1"
+
+ # Extract Info.plist to a temporary file
+ local temp_plist=$(mktemp)
+ unzip -p "$ipa_path" "Payload/*.app/Info.plist" > "$temp_plist"
+
+ # Read version and build from the plist
+ VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$temp_plist")
+ BUILD=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$temp_plist")
+
+ # Clean up temp file
+ rm -f "$temp_plist"
+}
+
+# ------------------------------------------
+# Ask if user wants to build or use existing
+# ------------------------------------------
+
+SCHEME="AliasVault"
+WORKSPACE="AliasVault.xcworkspace"
+CONFIG="Release"
+ARCHIVE_PATH="$PWD/build/${SCHEME}.xcarchive"
+EXPORT_DIR="$PWD/build/export"
+EXPORT_PLIST="$PWD/exportOptions.plist"
+
+echo ""
+echo "What do you want to do?"
+echo " 1) Build and submit to TestFlight"
+echo " 2) Build only"
+echo " 3) Submit existing IPA to TestFlight"
+echo ""
+read -p "Enter choice (1, 2, or 3): " -r CHOICE
+echo ""
+
+# ------------------------------------------
+# Build IPA (for options 1 and 2)
+# ------------------------------------------
+
+if [[ $CHOICE == "1" || $CHOICE == "2" ]]; then
+ echo "Building IPA..."
+
+ # Clean + archive
+ xcodebuild \
+ -workspace "$WORKSPACE" \
+ -scheme "$SCHEME" \
+ -configuration "$CONFIG" \
+ -archivePath "$ARCHIVE_PATH" \
+ clean archive \
+ -allowProvisioningUpdates
+
+ # Export .ipa
+ rm -rf "$EXPORT_DIR"
+ xcodebuild -exportArchive \
+ -archivePath "$ARCHIVE_PATH" \
+ -exportOptionsPlist "$EXPORT_PLIST" \
+ -exportPath "$EXPORT_DIR" \
+ -allowProvisioningUpdates
+
+ IPA_PATH=$(ls "$EXPORT_DIR"/*.ipa)
+
+ # Extract version info from newly built IPA
+ extract_version_info "$IPA_PATH"
+ echo "IPA built at: $IPA_PATH"
+ echo " Version: $VERSION"
+ echo " Build: $BUILD"
+ echo ""
+
+ # Exit if build-only
+ if [[ $CHOICE == "2" ]]; then
+ echo "✅ Build complete. Exiting."
+ exit 0
+ fi
+fi
+
+# ------------------------------------------
+# Submit to TestFlight (for options 1 and 3)
+# ------------------------------------------
+
+if [[ $CHOICE == "3" ]]; then
+ # Use existing IPA
+ IPA_PATH="$EXPORT_DIR/AliasVault.ipa"
+
+ if [ ! -f "$IPA_PATH" ]; then
+ echo "❌ IPA file not found at: $IPA_PATH"
+ exit 1
+ fi
+
+ # Extract version info from existing IPA
+ extract_version_info "$IPA_PATH"
+ echo "Using existing IPA: $IPA_PATH"
+ echo " Version: $VERSION"
+ echo " Build: $BUILD"
+ echo ""
+fi
+
+if [[ $CHOICE != "1" && $CHOICE != "3" ]]; then
+ echo "❌ Invalid choice. Please enter 1, 2, or 3."
+ exit 1
+fi
+
+echo ""
+echo "================================================"
+echo "Submitting to TestFlight:"
+echo " Version: $VERSION"
+echo " Build: $BUILD"
+echo "================================================"
+echo ""
+read -p "Are you sure you want to push this to TestFlight? (y/n): " -r
+echo ""
+
+if [[ ! $REPLY =~ ^([Yy]([Ee][Ss])?|[Yy])$ ]]; then
+ echo "❌ Submission cancelled"
+ exit 1
+fi
+
+echo "Checking if build already exists on TestFlight..."
+
+# Get the latest TestFlight build number for this version
+set +e
+RAW_OUTPUT=$(fastlane run latest_testflight_build_number \
+ app_identifier:"$BUNDLE_ID" \
+ version:"$VERSION" \
+ api_key_path:"$API_KEY_PATH" \
+ 2>&1)
+set -e
+
+# Extract the build number from the output
+LATEST=$(echo "$RAW_OUTPUT" | grep -oE "Result: [0-9]+" | grep -oE "[0-9]+" | head -n1)
+
+# Check if we got a valid result
+if [ -z "$LATEST" ]; then
+ echo "❌ Failed to get TestFlight build number. Fastlane output:"
+ echo "$RAW_OUTPUT"
+ echo ""
+ echo "This could mean:"
+ echo " - No builds exist for version $VERSION on TestFlight (first upload)"
+ echo " - API authentication failed"
+ echo " - Network/API error"
+ exit 1
+fi
+
+echo "Latest TestFlight build number for version $VERSION: $LATEST"
+
+# Numeric compare - if latest >= current, it's a duplicate
+if [ "$LATEST" -ge "$BUILD" ]; then
+ echo "🚫 Duplicate detected: TestFlight already has $VERSION with build $LATEST (your build: $BUILD)."
+ exit 1
+fi
+
+echo "✅ No duplicate found. Proceeding with deliver..."
+
+fastlane deliver \
+ --ipa "$IPA_PATH" \
+ --skip_screenshots \
+ --skip_metadata \
+ --api_key_path "$API_KEY_PATH" \
+ --run_precheck_before_submit=false
diff --git a/apps/mobile-app/ios/exportOptions.plist b/apps/mobile-app/ios/exportOptions.plist
index 076df9616..0e634295d 100644
--- a/apps/mobile-app/ios/exportOptions.plist
+++ b/apps/mobile-app/ios/exportOptions.plist
@@ -12,5 +12,7 @@
compileBitcode
+ manageAppVersionAndBuildNumber
+
\ No newline at end of file