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
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
项目描述
一款用于追踪罗斯-霍尔曼理工学院加权课程成绩的 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 |