Skip to content

vishwas-kr/Fastgo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

87 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Fastgo Scooty - iOS Mobile App πŸ›΄

Fastgo Scooty is a modern, eco-friendly electric scooter rental application built with SwiftUI and powered by Supabase. This project implements the UI/UX design concept by Orbix Studio on Behance, focusing on a seamless user experience for urban mobility.


πŸ“± Features

Based on the Fastgo Scooty design specifications, the app includes:

Feature Status Description
Phone OTP Authentication βœ… Sign in with phone number + SMS OTP via Supabase
Onboarding Flow βœ… 3-page intro screens for new users
User Profile Management βœ… Name, gender, DOB, profile picture upload
Interactive Map βœ… MapKit integration with real-time location
Scooter Discovery βœ… View nearby available scooters on map
Scooter Details Sheet βœ… Battery, range, pricing info in bottom sheet
Navigation to Scooter βœ… Route polyline with ETA & distance
QR Code Scanner βœ… Camera-based QR scanning to unlock scooter
Ride Reservation βœ… 10-minute free reservation with countdown timer
Ride In-Progress βœ… Live ride tracking with parking spots displayed
Parking Navigation βœ… Navigate to designated parking areas
Ride Completion βœ… Finish ride flow with instructions
Ride History βœ… View past rides and trip details
Promo Codes βœ… Apply promotional discounts
Photo Verification βœ… Submit a photo of your parked scooter for completing ride
Payment Methods βœ… Save your bank or card details

🚧 Pending Features

  • Payment Integration - Coming soon

πŸ›  Tech Stack

  • Language: Swift 5+
  • Framework: SwiftUI
  • Architecture: MVVM (Model-View-ViewModel)
  • Backend: Supabase (PostgreSQL, Phone Auth, Realtime, Storage), Twilio (OTP SMS)
  • Maps: MapKit / CoreLocation
  • Hardware Integration: AVFoundation (QR Scanning)

πŸ“‚ Project Structure

Fastgo/
β”œβ”€β”€ FastgoApp.swift              # App entry point & RootView
β”œβ”€β”€ Model/
β”‚   β”œβ”€β”€ User.swift               # User profile models (UserProfile, UserStatus)
β”‚   β”œβ”€β”€ Scooter.swift            # Scooter & ScooterDTO models
β”‚   β”œβ”€β”€ ScooterAnnotation.swift  # Map annotation for scooters
β”‚   β”œβ”€β”€ ParkingAnnotation.swift  # Map annotation for parking spots
β”‚   β”œβ”€β”€ Ride.swift               # Ride model & RideStatus enum
β”‚   β”œβ”€β”€ OnboardingPage.swift     # Onboarding page content
β”‚   β”œβ”€β”€ Promo.swift              # Promo variants & data
β”‚   β”œβ”€β”€ FinishRide.swift         # Finish ride instructions data
β”‚   β”œβ”€β”€ ProfileOptions.swift     # Profile menu options
β”‚   └── Country.swift            # Country picker data
β”‚
β”œβ”€β”€ View/
β”‚   β”œβ”€β”€ Onboarding/              # Welcome/intro screens (3 pages)
β”‚   β”‚   β”œβ”€β”€ View/
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ SignIn/                  # Authentication flow
β”‚   β”‚   β”œβ”€β”€ View/                # SignInView, OTPView
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ BasicInfo/               # Profile setup after first login
β”‚   β”‚   β”œβ”€β”€ View/                # Name, Gender, DOB inputs
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ Home/                    # Main map screen
β”‚   β”‚   β”œβ”€β”€ View/                # HomeView, CustomAppBar, LocationButton
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ Map/                     # Map display
β”‚   β”‚   β”œβ”€β”€ View/                # MapView
β”‚   β”‚   └── ViewModel/           # MapViewModel (camera, location, geocoding)
β”‚   β”‚
β”‚   β”œβ”€β”€ RideDetails/             # Scooter detail bottom sheet
β”‚   β”‚   β”œβ”€β”€ View/                # RideDetailsView, ScooterRideActionFeatures
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ RideNavigation/          # Navigation & ride flow
β”‚   β”‚   β”œβ”€β”€ View/                # RideNavigationView, CancellationRideCard,
β”‚   β”‚   β”‚                        # InProgressRideCard, RideStateBottomCard,
β”‚   β”‚   β”‚                        # SlideToAction, AnnotationInfoCapsule
β”‚   β”‚   └── ViewModel/           # RideNavigationViewModel (ride state, routes)
β”‚   β”‚
β”‚   β”œβ”€β”€ QRCodeScanner/           # QR code scanning
β”‚   β”‚   β”œβ”€β”€ View/                # QRCodeScanView
β”‚   β”‚   β”œβ”€β”€ Controller/          # QRScannerController (AVFoundation)
β”‚   β”‚   └── ViewModel/
β”‚   β”‚
β”‚   β”œβ”€β”€ FinishRide/              # Ride completion screen
β”‚   β”‚   └── View/                # FinishRideView, FinishRideCard
β”‚   β”‚
β”‚   └── Profile/                 # User profile screens
β”‚       β”œβ”€β”€ View/                # ProfileView
β”‚       β”œβ”€β”€ SubViews/            # MyAccountView, RideHistoryView, etc.
β”‚       β”œβ”€β”€ Components/          # Profile UI components
β”‚       └── ViewModel/
β”‚
β”œβ”€β”€ Components/                  # Reusable UI components
β”‚   β”œβ”€β”€ ScooterAnnotationContent.swift    # Scooter map marker
β”‚   β”œβ”€β”€ ParkingAnnotationView.swift       # Parking map marker
β”‚   β”œβ”€β”€ UserLocationAnnotation.swift      # Custom user location marker
β”‚   β”œβ”€β”€ CustomGreenButton.swift           # Primary action button
β”‚   β”œβ”€β”€ CustomToolBarBackButton.swift     # Navigation back button
β”‚   β”œβ”€β”€ LottieAnimation.swift             # Lottie animation wrapper
β”‚   └── [Other reusable components]
β”‚
β”œβ”€β”€ Services/
β”‚   β”œβ”€β”€ SupabaseService.swift    # Auth, storage, session management
β”‚   β”œβ”€β”€ UserService.swift        # User CRUD operations
β”‚   β”œβ”€β”€ NavigationServices.swift # Fetch scooters & parking
β”‚   └── RideService.swift        # Ride operations
β”‚
β”œβ”€β”€ Utils/
β”‚   β”œβ”€β”€ Manager/
β”‚   β”‚   β”œβ”€β”€ AppStateManager.swift   # Global app state & flow control
β”‚   β”‚   β”œβ”€β”€ LocationManager.swift   # CLLocationManager wrapper
β”‚   β”‚   └── CacheManager.swift      # Local caching (user, images)
β”‚   β”œβ”€β”€ Extensions/              # Swift extensions
β”‚   └── Helpers/                 # Utility helpers
β”‚
β”œβ”€β”€ Routes/
β”‚   └── Router.swift             # HomeRouter, HomeRoutes, ProfileOptionRoutes
β”‚
β”œβ”€β”€ Constants/
β”‚   β”œβ”€β”€ APIConstants.swift       # Supabase URL & API key
β”‚   β”œβ”€β”€ AppConstants.swift       # Asset names, colors
β”‚   └── CountryConstants.swift   # Country codes for phone input
β”‚
└── Resources/
    └── Assets.xcassets          # Images, colors, app icon

πŸš€ Getting Started

Prerequisites

  • Xcode: Version 15.0 or later
  • iOS: Target iOS 17.0+
  • Supabase Account: A generic project created on Supabase
  • Twilio Account: A generic project created on Twilio

Installation

  1. Clone the repository

    git clone https://github.com/vishwas-kr/Fastgo.git
    cd fastgo
  2. Open in Xcode Open FastgoScooty.xcodeproj in Xcode.

  3. Configure Environment Variables Create a file named Secrets.plist (or use a .xcconfig file) in your project root to store your API keys.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "[http://www.apple.com/DTDs/PropertyList-1.0.dtd](http://www.apple.com/DTDs/PropertyList-1.0.dtd)">
    <plist version="1.0">
    <dict>
        <key>SupabaseURL</key>
        <string>YOUR_SUPABASE_PROJECT_URL</string>
        <key>SupabaseAnonKey</key>
        <string>YOUR_SUPABASE_ANON_KEY</string>
    </dict>
    </plist>

Supabase Database Setup

  • Run this SQL in the Supabase SQL Editor for creating a table Users:
CREATE TABLE users (
    id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
    phone VARCHAR(20) UNIQUE NOT NULL,
    name VARCHAR(100),
    gender VARCHAR(20),
    about_me VARCHAR(100),
    date_of_birth DATE,
    profile_image TEXT,
    total_rides INTEGER NOT NULL DEFAULT 0,
    total_distance DECIMAL(10,2) NOT NULL DEFAULT 0.0,
    user_status JSONB NOT NULL DEFAULT '{"basic_info_completed": false}'::jsonb,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
  • For Adding RLS Policy for Users Table:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can read their own profile"
ON users
FOR SELECT
USING (auth.uid() = id);

CREATE POLICY "Users can insert their own profile"
ON users
FOR INSERT
WITH CHECK (auth.uid() = id);

CREATE POLICY "Users can update their own profile"
ON users
FOR UPDATE
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);

CREATE POLICY "Users can delete their own profile"
ON users
FOR DELETE
USING (auth.uid() = id);
  • Run this SQL in the Supabase SQL Editor for creating a table Rides:
CREATE TYPE ride_status AS ENUM (
    'reserved',
    'in_progress',
    'completed',
    'cancelled'
);

CREATE TABLE rides (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    user_id UUID NOT NULL
        REFERENCES users(id)
        ON DELETE CASCADE,

    scooter_id UUID NOT NULL
        REFERENCES scooters(id),

    status ride_status NOT NULL DEFAULT 'reserved',

    -- Start
    start_latitude DOUBLE PRECISION NOT NULL,
    start_longitude DOUBLE PRECISION NOT NULL,
    start_location_name TEXT,

    started_at TIMESTAMPTZ,

    -- End (nullable until ride finishes)
    end_latitude DOUBLE PRECISION,
    end_longitude DOUBLE PRECISION,
    end_location_name TEXT,

    ended_at TIMESTAMPTZ,

    -- Metrics
    duration_minutes INT CHECK (duration_minutes >= 0),
    distance_km DECIMAL(10,2) CHECK (distance_km >= 0),

    -- Billing
    base_fare DECIMAL(10,2) NOT NULL DEFAULT 0,
    time_fare DECIMAL(10,2) NOT NULL DEFAULT 0,
    total_fare DECIMAL(10,2) NOT NULL DEFAULT 0,

    promo_code TEXT,
    discount_amount DECIMAL(10,2) DEFAULT 0,

    ride_completed_photo_url TEXT NULL;

    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
  • For Adding RLS Policy for Rides Table:
CREATE POLICY "Users can view their rides"
ON rides
FOR SELECT
USING (user_id = auth.uid());


CREATE POLICY "Users can create rides"
ON rides
FOR INSERT
WITH CHECK (user_id = auth.uid());


CREATE POLICY "Users can update active rides"
ON rides
FOR UPDATE
USING (
    user_id = auth.uid()
    AND status IN ('reserved', 'in_progress')
)
WITH CHECK (
    user_id = auth.uid()
);
  • Run this SQL in the Supabase SQL Editor for creating a table Scooters:
CREATE TABLE scooters (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    unique_code VARCHAR(50) UNIQUE NOT NULL, -- QR code value
    type scooter_type NOT NULL,

    battery INTEGER NOT NULL CHECK (battery BETWEEN 0 AND 100),
    range_km INTEGER NOT NULL CHECK (range_km >= 0),

    per_min_cost DECIMAL(10,2) NOT NULL,
    image_name TEXT NOT NULL,

    latitude DOUBLE PRECISION NOT NULL,
    longitude DOUBLE PRECISION NOT NULL,

    status scooter_status NOT NULL DEFAULT 'available',

    last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
  • For Adding RLS Policy for Scooters Table:
ALTER TABLE scooters ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Public can view available scooters"
ON scooters
FOR SELECT
USING (status = 'available');

CREATE POLICY "No direct scooter updates"
ON scooters
FOR UPDATE
USING (false);
  • Run this SQL in the Supabase SQL Editor for creating a table Parking:
create table parking (
    id uuid primary key default gen_random_uuid(),
    
    name text not null,
    
    latitude double precision not null,
    longitude double precision not null,
    
    created_at timestamp with time zone default now()
);
  • For Adding RLS Policy
alter table parking enable row level security;

create policy "Allow read access"
on parking
for select
using (true);
  • Run this SQL in the Supabase SQL Editor for creating a table Payments:
create table payment_methods (
    id TEXT PRIMARY KEY,
    user_id UUID REFERENCES auth.users(id) NOT NULL,
    type TEXT NOT NULL CHECK (type IN ('bank', 'card')),
    card_number TEXT,
    card_holder_name TEXT,
    expiry_date TEXT,
    cvv TEXT,
    bank_name TEXT,
    account_number TEXT,
    ifsc_code TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);
  • For Adding RLS Policy
ALTER TABLE payment_methods ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view their payment methods"
ON payment_methods FOR SELECT
USING (user_id = auth.uid());

CREATE POLICY "Users can insert payment methods"
ON payment_methods FOR INSERT
WITH CHECK (user_id = auth.uid());

CREATE POLICY "Users can delete their payment methods"
ON payment_methods FOR DELETE
USING (user_id = auth.uid());
  • Additional Setup :
  • Enable Phone Auth in Supabase Authentication settings and selectr Twilio as SMS provider. Add your Twilio Account SID , Twilio Auth Token & Twilio Message Service SID.
  • Create a storage bucket named avatars for profile images
  • Enable RLS Policy for 'avatars' bucket
CREATE POLICY "Users can upload their own avatar"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (
  bucket_id = 'avatars'
  AND auth.uid()::text = split_part(name, '/', 1)
);

CREATE POLICY "Public read access for avatars"
ON storage.objects
FOR SELECT
USING (bucket_id = 'avatars');

CREATE POLICY "Users can update their own avatar"
ON storage.objects
FOR UPDATE
TO authenticated
USING (
  bucket_id = 'avatars'
  AND auth.uid()::text = split_part(name, '/', 1)
);

  • Create a storage bucket named completedRides for profile images
  • Enable RLS Policy for 'completedRides' bucket
CREATE POLICY "Users can upload their own ride completion photo"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (
  bucket_id = 'completedRides'
  AND auth.uid()::text = split_part(name, '/', 1)
);

CREATE POLICY "Public read access for completed rides"
ON storage.objects
FOR SELECT
USING (bucket_id = 'completedRides');

CREATE POLICY "Users can update their own ride completion photo"
ON storage.objects
FOR UPDATE
TO authenticated
USING (
  bucket_id = 'completedRides'
  AND auth.uid()::text = split_part(name, '/', 1)
);

πŸ”„ App Flow

Authentication Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Onboarding  │───▢│   Sign In    │───▢│  Verify OTP  │───▢│  Basic Info  β”‚
β”‚  (3 pages)   β”‚    β”‚ (Phone no.)  β”‚    β”‚  (6 digits)  β”‚    β”‚ (Name, DOB)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                    β”‚
                                                                    β–Ό
                                                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                            β”‚   Home Map   β”‚
                                                            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Complete Ride Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              RIDE FLOW                                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  Browse  β”‚  User sees map with scooter markers
     β”‚   Mode   β”‚  
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Tap scooter marker
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ Scooter  β”‚  Bottom sheet shows scooter details:
     β”‚ Details  β”‚  β€’ Battery level, Range, Price/min
     β”‚  Sheet   β”‚  β€’ Navigate / Reserve options
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Tap "Navigate"
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ Reserved β”‚  β€’ Route polyline drawn to scooter
     β”‚  Status  β”‚  β€’ "Reserve now" button visible
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Tap "Reserve now"
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚Cancelled β”‚  β€’ 10-minute countdown timer starts
     β”‚(Reserved)β”‚  β€’ "Swipe to cancel" slider
     β”‚  Status  β”‚  β€’ "Scan QR" button to unlock
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Scan QR Code successfully
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚   In     β”‚  β€’ Scooter unlocked, ride begins
     β”‚ Progress β”‚  β€’ Parking spots appear on map
     β”‚  Status  β”‚  β€’ Duration & distance tracking
     β”‚          β”‚  β€’ "Swipe to finish ride" slider
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Tap parking marker (optional)
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ Navigate β”‚  Route polyline to selected parking
     β”‚ Parking  β”‚  
     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
          β”‚ Swipe to finish
          β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ Finish   β”‚  β€’ Instructions displayed
     β”‚  Ride    β”‚  β€’ Photo verification (pending)
     β”‚  View    β”‚  β€’ Return to home
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Ride Status State Machine

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ .reserved β”‚ ◀──────────────┐
                    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                β”‚
                          β”‚                      β”‚
            Tap "Reserve" β”‚                      β”‚ Cancel / Back
                          β–Ό                      β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚
                    β”‚.cancelled β”‚ β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚(awaiting  β”‚
                    β”‚  QR scan) β”‚
                    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                          β”‚
               QR Scan OK β”‚
                          β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚.inProgressβ”‚
                    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                          β”‚
           Swipe to finishβ”‚
                          β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚.completed β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

App Screenshots

Onboarding Login CountryCode OTP
User Name DOB Gender Success
Maps Permission Home Scooter Detail QR Code Sacan
Settings Profile Ride History Promo Codes
Invite/Share Payment Bank Payment Cards
Navigate to Scooter Reserve Scooter Ride In Progress Nearby Parking
Finish Ride Camera Ride Completion Verification

Releases

No releases published

Packages

 
 
 

Contributors

Languages