Skip to content

πŸ“± iOS Instagram Clone - SwiftUI & Firebase Tutorial A fully functional Instagram clone built with SwiftUI and Firebase, featuring modern iOS development patterns and complete social media functionality. This tutorial project demonstrates building a production-ready social media app with authentication, real-time data, image upload, and intera

Notifications You must be signed in to change notification settings

dmakarau/InstagramClone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

44 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Instagram Clone - SwiftUI

A comprehensive iOS Instagram clone built with SwiftUI. This project explores modern iOS development patterns, Firebase integration, and contemporary app architecture while building a fully functional social media app.

Technologies & Frameworks

SwiftUI Firebase iOS Swift Xcode MVVM
Observation Framework Async/Await PhotosPicker NavigationStack

Core Technologies:

  • SwiftUI - Declarative UI framework with iOS 17+ Observable architecture
  • Firebase - Authentication, Firestore database, and Storage (SDK 12.1.0+)
  • Swift Concurrency - Async/await patterns with MainActor
  • Observation Framework - Modern reactive programming (replaces Combine)
  • PhotosPicker - Native photo selection for iOS

App Screenshots

Login Screen
Login Screen
Feed Screen
Feed Screen
Search Screen
Search & Discovery
Profile Screen
User Profile

Project Overview

This Instagram clone demonstrates modern iOS development through practical implementation. The app includes all the core features you'd expect from a social media platform - user authentication, real-time feeds, commenting, notifications, and user relationships.

The project focuses on:

  • SwiftUI Framework for modern declarative UI development
  • Firebase Integration for authentication, database, and cloud storage
  • Real-time data synchronization using async/await patterns
  • MVVM architecture with the new Observation framework
  • Modern iOS features like PhotosPicker and NavigationStack

Architecture & Design Patterns

The app follows a clean MVVM architecture using SwiftUI and the new iOS 17+ Observation framework. Here's how it's structured:

SwiftUI + MVVM Architecture

  • View Layer: SwiftUI views with declarative UI components using State and Bindable
  • ViewModel Layer: Observable classes using iOS 17+ Observation framework (no Combine needed)
  • Service Layer: Singleton Observable services for Firebase operations and data management
  • Model Layer: Codable data models that work seamlessly with Firebase

Technology Stack

  • SwiftUI for the entire user interface
  • Firebase SDK 12.1.0 for backend services (Auth, Firestore, Storage)
  • iOS 17+ Observation framework for reactive data binding
  • PhotosPicker for native photo selection
  • Async/Await for modern Swift concurrency

Registration Flow (Router-Driven)

  • Uses AuthenticationRouter (@Observable) to centralize multi-step onboarding.
  • RegistrationSteps (Identifiable, Hashable) drives NavigationStack(path:) destinations.
  • Views call router.startRegistration() to begin and router.navigate() to advance steps.
  • LoginView provides RegistrationViewModel and AuthenticationRouter via environment and uses navigationDestination(for:).
  • Real-time validation: AddEmailView and CreateUsernameView validate uniqueness via AuthManager.validateEmail() and validateUsername() before advancing to next step.

Error Handling

  • Custom AuthenticationError enum with Firebase error code mapping (17004-17026)
  • User-friendly localized error messages for all authentication failures
  • ViewModels use showError Boolean and error: AuthenticationError? with didSet observer
  • Views present alerts automatically using .alert("Oops!", isPresented: $viewModel.showError)

Example:

@State private var router = AuthenticationRouter()

NavigationStack(path: $router.navigationPath) {
    // ... Login UI ...
}
.navigationDestination(for: RegistrationSteps.self) { step in
    switch step {
    case .email: AddEmailView()
    case .username: CreateUsernameView()
    case .password: CreatePasswordView()
    case .completion: CompleteSighUpView()
    }
}

// Start registration from LoginView
Button("Sign up") { router.startRegistration() }

// Advance step inside registration views
Button("Next") { router.navigate() }

What's Implemented

This isn't just a basic clone - it's a fully functional social media app with all the features you'd expect:

User Authentication & Profiles Complete user system with email/password registration, multi-step onboarding, and profile management. Users can upload profile pictures, edit their information, and view detailed statistics about their activity.

Social Features Full follow/unfollow system with real-time updates. Users can see their followers and following lists, discover new users through search, and view other profiles with complete post grids.

Posts & Feed Instagram-style feed with real-time loading, photo uploading via PhotosPicker, and complete interaction system. Users can like, unlike, and comment on posts with optimistic UI updates for smooth performance.

Real-time Notifications Comprehensive notification system that alerts users when someone likes their posts, comments, or follows them. Notifications are delivered in real-time with proper Firebase security rules.

Comments System Full commenting functionality with real-time updates. Comments appear instantly with optimistic UI while syncing to Firebase in the background.

Search & Discovery Real-time user search with live filtering and responsive results. The app includes some fun character profiles (Marvel characters) for demonstration purposes.

App Structure

InstagramCloneTutorial/
β”œβ”€β”€ App/
β”‚   └── InstagramCloneTutorialApp.swift           # App entry point with Firebase configuration
β”œβ”€β”€ Core/
β”‚   β”œβ”€β”€ Authentication/                           # Complete authentication system
β”‚   β”‚   β”œβ”€β”€ Error/AuthenticationError.swift       # Custom error enum with Firebase error code mapping
β”‚   β”‚   β”œβ”€β”€ Service/AuthService.swift             # Firebase Auth integration
β”‚   β”‚   β”œβ”€β”€ ViewModel/                            # LoginViewModel, RegistrationViewModel
β”‚   β”‚   └── View/                                # Login, Registration flow screens
β”‚   β”œβ”€β”€ Routing/                                  # Registration flow routing
β”‚   β”‚   β”œβ”€β”€ AuthenticationRouter.swift            # Observable router with navigationPath
β”‚   β”‚   └── RegistrationSteps.swift               # Step enum driving NavigationStack
β”‚   β”œβ”€β”€ Feed/                                    # Main timeline functionality
β”‚   β”‚   β”œβ”€β”€ View/FeedView.swift, FeedCell.swift  # Feed UI components
β”‚   β”‚   └── ViewModel/FeedViewModel.swift         # Feed data management
β”‚   β”œβ”€β”€ Comments/                                # Comments system
β”‚   β”‚   β”œβ”€β”€ Model/Comment.swift                  # Comment data model
β”‚   β”‚   β”œβ”€β”€ Service/CommentService.swift         # Firebase comment operations
β”‚   β”‚   β”œβ”€β”€ View/CommentsView.swift, CommentsCell.swift  # Comment UI components
β”‚   β”‚   └── ViewModel/CommentViewModel.swift      # Comment data management
β”‚   β”œβ”€β”€ Notifications/                           # Real-time notifications system
β”‚   β”‚   β”œβ”€β”€ Model/                               # IGNotification.swift, IGNotificationType.swift
β”‚   β”‚   β”œβ”€β”€ Service/NotificationsService.swift   # Firebase notification operations
β”‚   β”‚   β”œβ”€β”€ Manager/NotificationManager.swift    # Centralized notification management
β”‚   β”‚   β”œβ”€β”€ View/                                # NotificationsView.swift, NotificationsCell.swift
β”‚   β”‚   └── ViewModel/IGNotificationsViewModel.swift  # Notification data management
β”‚   β”œβ”€β”€ Search/                                  # User discovery system
β”‚   β”‚   β”œβ”€β”€ View/SearchView.swift                # Search interface
β”‚   β”‚   └── ViewModel/SearchViewModel.swift       # Search functionality
β”‚   β”œβ”€β”€ Profile/                                 # Complete profile system
β”‚   β”‚   β”œβ”€β”€ View/                                # ProfileView, EditProfileView, ProfileHeaderView, etc.
β”‚   β”‚   └── ViewModel/
β”‚   β”‚       β”œβ”€β”€ EditProfileViewModel.swift       # Profile editing logic
β”‚   β”‚       └── ProfileViewModel.swift           # Profile data, follow/unfollow, stats management
β”‚   β”œβ”€β”€ UploadPosts/                             # Post creation system
β”‚   β”‚   β”œβ”€β”€ View/UploadPostView.swift            # Post upload interface
β”‚   β”‚   └── ViewModel/UploadPostViewModel.swift   # Upload functionality
β”‚   β”œβ”€β”€ Components/                              # Reusable UI components
β”‚   β”‚   β”œβ”€β”€ View/CircularProfileImageView.swift  # Profile image component
β”‚   β”‚   β”œβ”€β”€ View/PostGridView.swift              # Post grid display
β”‚   β”‚   β”œβ”€β”€ View/UserStatView.swift              # User statistics display
β”‚   β”‚   β”œβ”€β”€ View/UserListView.swift              # User list component for followers/following
β”‚   β”‚   β”œβ”€β”€ ViewModel/PostGridViewModel.swift     # Grid data management
β”‚   β”‚   └── ViewModel/UserListViewModel.swift     # User list data management
β”‚   β”œβ”€β”€ TabBar/MainTabView.swift                 # Main navigation container
β”‚   └── Root/                                    # App root management
β”‚       β”œβ”€β”€ View/ContentView.swift               # Root content view
β”‚       └── ViewModel/ContentViewModel.swift      # App state management
β”œβ”€β”€ Model/
β”‚   β”œβ”€β”€ User.swift                               # User data model with Firebase integration, follow status, and stats
β”‚   β”œβ”€β”€ Post.swift                               # Post data model with user linking
β”‚   └── UserListConfig.swift                     # Configuration enum for different user list contexts
β”œβ”€β”€ Services/
β”‚   β”œβ”€β”€ UserService.swift                        # Singleton service for user data, follow/unfollow, and stats
β”‚   β”œβ”€β”€ PostService.swift                        # Post data operations
β”‚   └── ImageUploader.swift                      # Firebase Storage image handling
β”œβ”€β”€ Extension/
β”‚   └── Timestamp.swift                          # Firebase Timestamp extension for relative time formatting
β”œβ”€β”€ Utils/
β”‚   └── Constants.swift                          # Firebase constants (users, posts, following, followers collections)
β”œβ”€β”€ Assets.xcassets/                             # App icons and character images
└── Screenshots/                                 # App screenshot collection

Technical Implementation

Firebase Integration

// Firebase constants for centralized collection references
struct FirebaseConstants {
    static let Root = Firestore.firestore()
    static let UsersCollection = Root.collection("users")
    static let PostsCollection = Root.collection("posts")
    static let FollowingCollection = Root.collection("following")
    static let FollowersCollection = Root.collection("followers")
    static let NotificationsCollection = Root.collection("notifications")
    
    static func UserNotificationCollection(uid: String) -> CollectionReference {
        return NotificationsCollection.document(uid).collection("user-notifications")
    }
}

// Authentication with async/await and custom error handling
func createUser(email: String, password: String, username: String) async throws {
    do {
        let result = try await Auth.auth().createUser(withEmail: email, password: password)
        await uploadUserData(uid: result.user.uid, username: username, email: email)
    } catch {
        let authErrorCode = AuthErrorCode(rawValue: (error as NSError).code)?.rawValue ?? -1
        throw AuthenticationError(rawValue: authErrorCode)
    }
}

// Custom AuthenticationError enum
enum AuthenticationError: Error {
    case userDisabled
    case emailAlreadyInUse
    case invalidEmail
    case wrongPassword
    case userNotFound
    case networkError
    case credentialAlreadyInUse
    case weakPassword
    case unknownError
    case invalidCredential
    case tooManyRequests
    
    init(rawValue: Int) {
        switch rawValue {
        case 17004: self = .invalidCredential  // Modern Firebase error
        case 17005: self = .userDisabled
        case 17007: self = .emailAlreadyInUse
        case 17008: self = .invalidEmail
        case 17009: self = .wrongPassword
        case 17010: self = .tooManyRequests
        case 17011: self = .userNotFound
        case 17020: self = .networkError
        case 17025: self = .credentialAlreadyInUse
        case 17026: self = .weakPassword
        default: self = .unknownError
        }
    }
}

// ViewModel error handling pattern
@Observable
class LoginViewModel {
    var email = ""
    var password = ""
    var showError = false
    var error: AuthenticationError? {
        didSet { showError = error != nil }
    }
    
    func login(with authManager: AuthManager) async {
        do {
            try await authManager.login(withEmail: email, password: password)
        } catch let error as AuthenticationError {
            self.error = error
        } catch {
            self.error = .unknownError
        }
    }
}

// View presents error alerts automatically
struct LoginView: View {
    @State var loginViewModel = LoginViewModel()
    
    var body: some View {
        // ... UI components ...
        .alert("Oops!", isPresented: $loginViewModel.showError) {
            Text(loginViewModel.error?.localizedDescription ?? "Unknown error")
        }
    }
}

// Firestore data operations with centralized constants
func fetchAllUsers() async throws -> [User] {
    let snapshot = try await FirebaseConstants.UsersCollection.getDocuments()
    return snapshot.documents.compactMap({ try? $0.data(as: User.self) })
}

// Centralized user state management
UserService.shared.currentUser // Accessible throughout the app

// Follow/Unfollow operations
static func follow(uid: String) async throws {
    // Creates subcollections in both following and followers collections
}

static func checkIfUserIsFollowed(uid: String) async throws -> Bool {
    // Checks if current user follows the target user
}

// Fetch user statistics
static func fetchUserStats(uid: String) async throws -> UserStats {
    // Returns following count, followers count, and posts count
}

// Relative timestamp formatting
extension Timestamp {
    func timestampString() -> String {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.second, .minute, .hour, .day, .weekOfMonth]
        formatter.maximumUnitCount = 1
        formatter.unitsStyle = .abbreviated
        return formatter.string(from: self.dateValue(), to: Date()) ?? ""
    }
}

// Notification system integration
class NotificationManager {
    static let shared = NotificationManager()
    private let service = NotificationsService()
    
    func uploadLikeNotification(to uid: String, post: Post) async throws {
        try await service.uploadNotification(toUid: uid, type: .like, post: post)
    }
    
    func uploadFollowNotification(to uid: String) async throws {
        try await service.uploadNotification(toUid: uid, type: .follow)
    }
    
    func deleteLikeNotification(notificationOwner: String, post: Post) async {
        // Automatically removes notification when user unlikes
    }
}

// Real-time notification fetching
func fetchNotifications() async throws -> [IGNotification] {
    let snapshot = try await FirebaseConstants
        .UserNotificationCollection(uid: currentUid)
        .order(by: "timestamp", descending: true)
        .getDocuments()
    
    return snapshot.documents.compactMap({ try? $0.data(as: IGNotification.self)})
}

Modern SwiftUI Patterns (iOS 17+)

// @Observable replaces ObservableObject + @Published
@Observable
class FeedViewModel {
    var posts = [Post]()

    func fetchPosts() async throws {
        // Modern async data loading
    }
}

// Views use @State instead of @StateObject
struct FeedView: View {
    @State private var viewModel = FeedViewModel()

    var body: some View {
        ForEach(viewModel.posts) { post in
            FeedCell(post: post)
        }
    }
}

// @Bindable enables two-way binding with @Observable
struct EditProfileView: View {
    @State private var viewModel: EditProfileViewModel

    var body: some View {
        @Bindable var viewModel = viewModel
        TextField("Name", text: $viewModel.fullname)
    }
}

Reactive State Management

// Singleton @Observable services for centralized state
@Observable
class AuthService {
    var userSession: FirebaseAuth.User?
    static let shared = AuthService()
}

@Observable
class UserService {
    var currentUser: User?
    static let shared = UserService()
}

// Views automatically observe @Observable properties
struct ContentView: View {
    @State var viewModel = ContentViewModel()

    var body: some View {
        if viewModel.userSession == nil {
            LoginView()
        } else if let currentUser = viewModel.currentUser {
            MainTabView(user: currentUser)
        }
    }
}

Getting Started

What you'll need:

  • Xcode 16.4 or later
  • iOS 18.5+ deployment target
  • A Firebase project (free to set up)

Setup:

  1. Clone this repository
  2. Open InstagramCloneTutorial.xcodeproj in Xcode
  3. Set up Firebase:
    • Create a new Firebase project at Firebase Console
    • Download your GoogleService-Info.plist file and add it to the project root
    • Update the bundle ID to match your Firebase project
    • Enable Authentication and Firestore in the Firebase Console
  4. Build and run

Important: The GoogleService-Info.plist file is gitignored for security. You'll need to add your own Firebase configuration file.

Firebase Setup: The app uses three Firebase services:

  • FirebaseAuth for user authentication
  • FirebaseFirestore for the database
  • FirebaseStorage for image uploads

What You'll Learn

Building this Instagram clone covers a lot of ground in modern iOS development:

  • Modern iOS Development with SwiftUI and the new Observable framework
  • Firebase Integration for real-time apps with authentication and database operations
  • Swift Concurrency using async/await patterns and MainActor for UI updates
  • MVVM Architecture using Observable classes instead of Combine
  • State Management with SwiftUI's State, Bindable, and Environment
  • Real-world UI/UX patterns that feel native to iOS

Current Status

This is a fully functional Instagram clone with all core features implemented. The app includes user authentication, profiles, posting, commenting, following, real-time notifications, and search functionality. Everything is built using modern iOS patterns and the latest SwiftUI features.

What's working: Everything! The app is feature-complete with user auth, posts, comments, follows, notifications, and search.

Future ideas:

  • Push notifications when the app is closed
  • Stories functionality
  • Direct messaging
  • Image filters and editing tools
  • Better image caching and performance optimization

Learning Resources

This project demonstrates concepts from modern iOS development, including SwiftUI patterns, Firebase integration, and Swift concurrency. It's a practical example of building a real-world social media app with contemporary tools and patterns.


This project demonstrates modern iOS development patterns and is designed for educational purposes.

About

πŸ“± iOS Instagram Clone - SwiftUI & Firebase Tutorial A fully functional Instagram clone built with SwiftUI and Firebase, featuring modern iOS development patterns and complete social media functionality. This tutorial project demonstrates building a production-ready social media app with authentication, real-time data, image upload, and intera

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages