Grade Tracker iOS Application 成绩追踪 iOS 应用程序

iOS mobile app for tracking weighted course grades at Rose-Hulman 用于追踪罗斯-霍尔曼加权课程成绩的iOS移动应用

iOS Application Development, Firebase, Firestore, Storage, Cloud Function, CRUD

View This Project on GitHub

Project Description

An iOS mobile application for tracking weighted course grades at Rose-Hulman Institute of Technology. The app enables students to monitor academic performance in real-time without waiting for professors to update online grade portals. Built as part of the CSSE484 iOS App Development course, this application demonstrates proficiency in Swift, UIKit, and Firebase backend services.

Key Features:

  • Rosefire Authentication: Secure single sign-on using Rose-Hulman’s Rosefire authentication system
  • CRUD Operations: Create, Read, Update, Delete courses and assignments with real-time synchronization
  • Weighted Grade Calculation: Automatic computation of course grades based on customizable category weights
  • Real-time Cloud Sync: Live data synchronization across devices via Firestore snapshot listeners
  • GPA Tracking: Automatic cumulative GPA calculation across all enrolled courses
  • Internationalization: Support for multiple languages including English and Chinese

Demo Video

System Architecture

The application follows a clean Model-View-Controller (MVC) architecture with a singleton-based service layer for Firebase interactions.

graph TB
    subgraph iOS["iOS Application (Swift + UIKit)"]
        subgraph Views["View Layer"]
            SB[Storyboards]
            CELLS[Custom Table Cells<br/>CourseTableCell]
        end

        subgraph Controllers["Controller Layer"]
            LOGIN_VC[LogInViewController]
            LIST_VC[CourseListViewController]
            DETAIL_VC[CourseDetailViewController]
            ADD_VC[AddCourseViewController]
            SIDE_VC[SideMenuViewController]
            GRADE_VC[GradeSideViewController]
        end

        subgraph Models["Model Layer"]
            COURSE[Course Model]
            ENUMS[UsefulEnums]
            CONST[Constants]
        end

        subgraph Managers["Service Layer (Singletons)"]
            AUTH_MGR[AuthManager]
            COURSE_COL[CoursesCollectionManager]
            COURSE_DOC[CourseDocumentManager]
            USER_DOC[UserDocumentManager]
            STORAGE_MGR[StorageManager]
            CLOUD_MGR[CloudFunctionsManager]
        end
    end

    subgraph Firebase["Firebase Backend"]
        AUTH[Firebase Auth<br/>+ Rosefire]
        FIRESTORE[(Firestore Database)]
        STORAGE[Cloud Storage]
        FUNCTIONS[Cloud Functions]
    end

    SB --> Controllers
    Controllers --> Managers
    Managers --> Models
    AUTH_MGR --> AUTH
    COURSE_COL --> FIRESTORE
    COURSE_DOC --> FIRESTORE
    USER_DOC --> FIRESTORE
    STORAGE_MGR --> STORAGE
    CLOUD_MGR --> FUNCTIONS

    style Views fill:#e1f5ff
    style Controllers fill:#fff4e1
    style Models fill:#d4edda
    style Managers fill:#fce4ec
    style Firebase fill:#f3e5f5

Class Structure

Data Model - Course

The Course class encapsulates all information about an academic course including grades and weights:

classDiagram
    class Course {
        +String name
        +String number
        +Int section
        +Int quarter
        +Int year
        +Int credit
        +String? documentId
        +Int? assignmentsWeight
        +Int? examsWeight
        +Int? labsWeight
        +Int? quizzesWeight
        +Int? partWeight
        +Double? partGrade
        +Array~Tuple~ assignmentsGrade
        +Array~Tuple~ examsGrade
        +Array~Tuple~ labsGrade
        +Array~Tuple~ quizzesGrade
        +init(name, number, section, quarter, year, credits)
        +init(snapshot: DocumentSnapshot)
    }

    class AuthManager {
        -static shared: AuthManager
        +currentUser: User?
        +isSignedIn: Bool
        +addLoginObserver(callback)
        +addLogoutObserver(callback)
        +removeObserver(handle)
        +signInNewEmailPasswordUser(email, password)
        +signInWithRosefireToken(token)
        +signOut()
    }

    class CoursesCollectionManager {
        -static shared: CoursesCollectionManager
        -coursesRef: CollectionReference
        +add(course: Course)
        +delete(documentId: String)
        +startListening(studentId, changeListener)
        +stopListening()
    }

    class CourseDocumentManager {
        -static shared: CourseDocumentManager
        +latestCourse: Course?
        +startListening(changeListener)
        +stopListening()
        +updateGrade(type, grades)
        +removeGrade(type)
        +updateWeight(type, weight)
        +updateParticipation(grade, weight)
    }

    class UserDocumentManager {
        -static shared: UserDocumentManager
        +currentGPA: Double
        +startListening(changeListener)
        +stopListening()
        +updateGPA(gpa)
    }

    CoursesCollectionManager --> Course : manages
    CourseDocumentManager --> Course : updates

Firestore Data Structure

erDiagram
    USERS ||--o{ COURSES : enrolls
    USERS {
        string uid PK
        string name
        double currentGPA
        string role
    }
    COURSES {
        string documentId PK
        string courseName
        string courseNumber
        int section
        int quarter
        int year
        int credit
        string takenBy FK
        int assignmentsWeight
        int examsWeight
        int labsWeight
        int quizzesWeight
        int participationWeight
    }
    COURSES ||--o{ ASSIGNMENTS : contains
    COURSES ||--o{ EXAMS : contains
    COURSES ||--o{ LABS : contains
    COURSES ||--o{ QUIZZES : contains
    ASSIGNMENTS {
        string displayName
        int weight
        double grade
    }
    EXAMS {
        string displayName
        int weight
        double grade
    }
    LABS {
        string displayName
        int weight
        double grade
    }
    QUIZZES {
        string displayName
        int weight
        double grade
    }

Sequence Diagrams

User Authentication Flow

sequenceDiagram
    participant User
    participant LoginVC as LogInViewController
    participant Rosefire
    participant AuthMgr as AuthManager
    participant Firebase as Firebase Auth

    User->>LoginVC: Tap "Sign In with Rosefire"
    LoginVC->>Rosefire: Request authentication
    Rosefire->>User: Show Rose-Hulman login page
    User->>Rosefire: Enter credentials
    Rosefire-->>LoginVC: Return Rosefire token
    LoginVC->>AuthMgr: signInWithRosefireToken(token)
    AuthMgr->>Firebase: signIn(withCustomToken:)
    Firebase-->>AuthMgr: Authentication result
    AuthMgr-->>LoginVC: Login observer callback
    LoginVC->>LoginVC: Navigate to CourseListViewController

Course List Loading Flow

sequenceDiagram
    participant VC as CourseListViewController
    participant CourseMgr as CoursesCollectionManager
    participant UserMgr as UserDocumentManager
    participant Firestore

    VC->>VC: viewWillAppear()
    VC->>CourseMgr: startListening(studentId, changeListener)
    CourseMgr->>Firestore: Query courses where takenBy == studentId
    Firestore-->>CourseMgr: Snapshot listener attached

    VC->>UserMgr: startListening(changeListener)
    UserMgr->>Firestore: Listen to user document
    Firestore-->>UserMgr: Snapshot listener attached

    Note over Firestore: Data changes occur

    Firestore-->>CourseMgr: Snapshot update
    CourseMgr->>CourseMgr: Parse courses by quarter
    CourseMgr-->>VC: changeListener(courses)
    VC->>VC: updateView()
    VC->>VC: Calculate GPA
    VC->>VC: Reload table view

Add New Course Flow

sequenceDiagram
    participant User
    participant AddVC as AddCourseViewController
    participant CourseMgr as CoursesCollectionManager
    participant Firestore

    User->>AddVC: Fill course details
    User->>AddVC: Set grade category weights
    User->>AddVC: Tap "Save"

    AddVC->>AddVC: Validate inputs
    AddVC->>AddVC: Create Course object

    AddVC->>CourseMgr: add(course)
    CourseMgr->>Firestore: addDocument(data)
    Firestore-->>CourseMgr: Document created
    CourseMgr->>Firestore: updateData(weights)
    Firestore-->>CourseMgr: Weights updated

    CourseMgr-->>AddVC: Success
    AddVC->>AddVC: Dismiss and return to list

Grade Entry Flow

sequenceDiagram
    participant User
    participant DetailVC as CourseDetailViewController
    participant GradeVC as GradeSideViewController
    participant DocMgr as CourseDocumentManager
    participant Firestore

    User->>DetailVC: Select grade category
    DetailVC->>GradeVC: Present grade entry view

    User->>GradeVC: Enter grade details
    User->>GradeVC: Tap "Save"

    GradeVC->>DocMgr: updateGrade(type, grades)
    DocMgr->>Firestore: updateData(gradeArray)
    Firestore-->>DocMgr: Update confirmed

    Note over Firestore: Snapshot listener triggers

    Firestore-->>DocMgr: Document snapshot
    DocMgr->>DocMgr: Parse updated course
    DocMgr-->>DetailVC: changeListener(course)
    DetailVC->>DetailVC: Recalculate weighted average
    DetailVC->>DetailVC: Update UI

Delete Course Flow

sequenceDiagram
    participant User
    participant ListVC as CourseListViewController
    participant CourseMgr as CoursesCollectionManager
    participant Firestore

    User->>ListVC: Swipe left on course row
    ListVC->>ListVC: Show delete confirmation

    User->>ListVC: Confirm delete
    ListVC->>CourseMgr: delete(documentId)
    CourseMgr->>Firestore: deleteDocument(documentId)
    Firestore-->>CourseMgr: Document deleted

    Note over Firestore: Snapshot listener triggers

    Firestore-->>CourseMgr: Updated snapshot
    CourseMgr-->>ListVC: changeListener(updatedCourses)
    ListVC->>ListVC: Reload table view

Technical Implementation Details

Manager Layer (Singleton Pattern)

The application uses a singleton-based service layer to manage Firebase interactions:

Manager Responsibility
AuthManager Handles user authentication via Firebase Auth and Rosefire SSO
CoursesCollectionManager Manages the courses collection with CRUD operations
CourseDocumentManager Handles individual course document updates (grades, weights)
UserDocumentManager Manages user profile data and GPA
StorageManager Handles file uploads/downloads to Firebase Storage
CloudFunctionsManager Invokes Firebase Cloud Functions for server-side logic

Real-time Data Synchronization

The app uses Firestore snapshot listeners for real-time updates:

flowchart LR
    subgraph Client["iOS Client"]
        LISTENER[Snapshot Listener]
        UI[UI Components]
    end

    subgraph Cloud["Firebase Cloud"]
        FIRESTORE[(Firestore)]
    end

    FIRESTORE -->|"Real-time updates"| LISTENER
    LISTENER -->|"Callback"| UI
    UI -->|"Write operations"| FIRESTORE

    style LISTENER fill:#e1f5ff
    style FIRESTORE fill:#fff4e1

Grade Calculation Algorithm

The weighted grade calculation follows this formula:

\[\text{Final Grade} = \sum_{i=1}^{n} \left( \text{Category}_i \times \text{Weight}_i \right)\]

Where each category grade is computed as:

\[\text{Category}_i = \frac{\sum_{j=1}^{m} \text{Score}_{ij}}{\sum_{j=1}^{m} \text{MaxScore}_{ij}} \times 100\]

Grade Categories:

  • Assignments (configurable weight)
  • Exams (configurable weight)
  • Labs (configurable weight)
  • Quizzes (configurable weight)
  • Participation (configurable weight)

Technical Stack

Component Technology
Language Swift 5
UI Framework UIKit with Storyboards
Architecture MVC (Model-View-Controller)
Backend Firebase (Auth, Firestore, Storage, Functions)
Authentication Rosefire SSO + Firebase Custom Tokens
Data Persistence Firestore (cloud) + UserDefaults (local cache)
Dependency Management CocoaPods

View Controllers

Controller Purpose
LogInViewController Handles Rosefire authentication flow
CourseListViewController Displays courses organized by quarter with GPA
CourseDetailViewController Shows course details and grade breakdown
AddCourseViewController Form for creating new courses with weights
GradeSideViewController Slide-in panel for entering grades
SideMenuViewController Navigation menu with settings and logout

iOS 应用开发, Firebase, Firestore, 存储, 云函数, CRUD

在 GitHub 上查看此项目

项目描述

一款用于追踪罗斯-霍尔曼理工学院加权课程成绩的 iOS 移动应用程序。该应用使学生能够实时监控学业表现,无需等待教授更新在线成绩门户。作为 CSSE484 iOS 应用开发课程的一部分,此应用展示了 Swift、UIKit 和 Firebase 后端服务的熟练使用。

主要功能:

  • Rosefire 身份验证:使用罗斯-霍尔曼的 Rosefire 身份验证系统进行安全单点登录
  • CRUD 操作:创建、读取、更新、删除课程和作业,支持实时同步
  • 加权成绩计算:根据可自定义的类别权重自动计算课程成绩
  • 实时云同步:通过 Firestore 快照监听器实现跨设备实时数据同步
  • GPA 追踪:自动计算所有已注册课程的累计 GPA
  • 国际化:支持多种语言,包括英语和中文

演示视频

系统架构

该应用程序遵循清晰的模型-视图-控制器 (MVC) 架构,并使用基于单例的服务层进行 Firebase 交互。

graph TB
    subgraph iOS["iOS 应用 (Swift + UIKit)"]
        subgraph Views["视图层"]
            SB[故事板]
            CELLS[自定义表格单元格]
        end

        subgraph Controllers["控制器层"]
            LOGIN_VC[登录视图控制器]
            LIST_VC[课程列表视图控制器]
            DETAIL_VC[课程详情视图控制器]
            ADD_VC[添加课程视图控制器]
        end

        subgraph Models["模型层"]
            COURSE[课程模型]
        end

        subgraph Managers["服务层 (单例)"]
            AUTH_MGR[身份验证管理器]
            COURSE_MGR[课程管理器]
            USER_MGR[用户管理器]
        end
    end

    subgraph Firebase["Firebase 后端"]
        AUTH[Firebase 身份验证]
        FIRESTORE[(Firestore 数据库)]
        STORAGE[云存储]
    end

    SB --> Controllers
    Controllers --> Managers
    Managers --> Models
    AUTH_MGR --> AUTH
    COURSE_MGR --> FIRESTORE
    USER_MGR --> FIRESTORE

    style Views fill:#e1f5ff
    style Controllers fill:#fff4e1
    style Models fill:#d4edda
    style Managers fill:#fce4ec

用户认证流程

sequenceDiagram
    participant 用户
    participant 登录VC as 登录视图控制器
    participant Rosefire
    participant 认证管理器
    participant Firebase

    用户->>登录VC: 点击"使用 Rosefire 登录"
    登录VC->>Rosefire: 请求认证
    Rosefire->>用户: 显示罗斯-霍尔曼登录页面
    用户->>Rosefire: 输入凭据
    Rosefire-->>登录VC: 返回 Rosefire 令牌
    登录VC->>认证管理器: 使用令牌登录
    认证管理器->>Firebase: 自定义令牌登录
    Firebase-->>认证管理器: 认证结果
    认证管理器-->>登录VC: 登录回调
    登录VC->>登录VC: 导航到课程列表

成绩计算算法

加权成绩计算遵循以下公式:

\[\text{最终成绩} = \sum_{i=1}^{n} \left( \text{类别}_i \times \text{权重}_i \right)\]

其中每个类别成绩计算为:

\[\text{类别}_i = \frac{\sum_{j=1}^{m} \text{得分}_{ij}}{\sum_{j=1}^{m} \text{满分}_{ij}} \times 100\]

技术栈

组件 技术
语言 Swift 5
UI 框架 UIKit + 故事板
架构 MVC (模型-视图-控制器)
后端 Firebase (身份验证, Firestore, 存储, 云函数)
认证 Rosefire SSO + Firebase 自定义令牌
数据持久化 Firestore (云端) + UserDefaults (本地缓存)
依赖管理 CocoaPods