一款基于SwiftUI的iOS应用程序,模拟了Twitter的核心功能——注册、登录、发布推文、点赞、关注、编辑个人资料、搜索、查看通知等。该应用程序使用Swift编写,采用MVVM架构,并使用(DI)依赖注入来管理其服务。
- 用户认证:使用电子邮件、用户名和密码注册和登录
- 个人资料管理:查看和编辑个人资料(头像、横幅、简介、位置、网站)
- 发布推文:创建推文并附加图片(可选)
- 点赞/取消点赞:点赞或取消点赞推文,并实时更新
- 通知:显示列表,列出他人点赞或关注的通知
- 搜索:按用户名或真实姓名搜索其他用户
- 消息(仅UI):简单的UI占位符用于直接消息
- 注销:清除会话令牌并返回WelcomeView
- Xcode 14+(或更新版本)
- iOS 16+ 部署目标
- Swift 5.7+
- 使用Swift包管理器(SPM)来获取诸如Kingfisher之类的依赖项
该应用程序配置为与位于 http://localhost:3000 的后端通信。您需要在该端口上运行一个本地服务器(或者您可以在 APIConfig.swift 中更改 URL)。
- 克隆代码库
git clone https://github.com/yourusername/TwitterClone.git
cd TwitterClone-
在Xcode中打开项目
- 双击 TwitterClone.xcodeproj 或通过 Xcode → "打开一个项目或文件…" 来打开
-
安装依赖项
- 该项目使用Swift包管理器(SPM)。Xcode将在首次打开时自动解析包
-
设置后端(可选)
- 确保您的后端服务器正在 localhost:3000 上运行。请参阅 后端设置 获取更多详情
-
运行
- 选择一个iOS模拟器,然后按 Run(⌘+R)运行
您应该会看到 WelcomeView,提示您 登录 或 创建账户。
代码库被组织成几个文件夹,每个文件夹包含特定领域的逻辑:
.
├─ App/ # 应用程序入口点和主场景
│ ├─ TwitterCloneApp.swift # 含 DIContainer 和 AuthState 的 App 结构体
│ └─ ContentView.swift # ContentView 决定显示哪个屏幕(MainView 或 WelcomeView)
│
├─ Core/ # 核心基础代码
│ ├─ Common/Extensions/ # 共享的扩展(例如:ImagePicker)
│ ├─ Legacy/ # 较旧或后备代码(例如:ImageUploader)
│ ├─ Network/ # 网络层
│ │ ├─ Base/ # APIClient、APIEndpoint、NetworkError、HTTPMethod
│ │ └─ Config/ # APIConfig,baseURL 等
│ └─ Storage/ # (用于Keychain/UserDefaults逻辑的占位符)
│
├─ Features/ # 基于功能的组织
│ ├─ Auth/ # 所有与认证相关的(登录、注册、AuthState、视图)
│ ├─ Feed/ # 推文创建、信息流展示、单元格UI等
│ ├─ Notifications/ # 通知列表和服务
│ ├─ Profile/ # 获取、编辑用户资料,上传头像/横幅
│ ├─ Messages/ # 简单的消息占位符
│ └─ Search/ # 用户搜索屏幕和逻辑
│
├─ Main/Views/ # 侧滑菜单(抽屉),主标签视图,顶部条等
├─ DIContainer.swift # 依赖注入容器
└─ ...
- TwitterCloneApp.swift
- 主要入口点,使用
@main。设置DIContainer和AuthState作为@StateObject
- 主要入口点,使用
- DIContainer.swift
- 使用基于字典的方式实现依赖注入。每个服务都在
ServiceType枚举键下注册(如.apiClient、.authService等)
- 使用基于字典的方式实现依赖注入。每个服务都在
- AuthState.swift
- 一个
@MainActor类,跟踪当前登录的用户,处理登录/登出,并发布认证状态
- 一个
- APIClient.swift
- 用于使用 async/await 进行HTTP请求的专门类。它可以发送JSON数据或多部分表单数据,处理重试、解码JSON等
- ImageUploader.swift
- 一个遗留的代码片段,用于手动上传图像(多部分表单数据),使用 URLSession
- ProfileViewModel.swift / ProfileView.swift
- 包含获取和编辑用户资料的逻辑,以及用户资料页面的UI
-
App
TwitterCloneApp.swift: 设置DIContainer.defaultContainer()和AuthState(authService:)ContentView.swift: 决定显示MainView(如果已认证)或WelcomeView(否则)
-
AuthState
- 被视图观察,用于确定用户是否已登录
- 提供
login()、register()、signOut()等方法,并更新全局currentUser
-
服务
AuthServiceProtocol + AuthService1:注册、登录、获取当前用户TweetServiceProtocol + TweetService:创建推文、点赞/取消点赞、上传图片ProfileServiceProtocol + ProfileService:获取和编辑资料、上传头像/横幅NotificationServiceProtocol + NotificationService:获取或创建通知
-
ViewModels
- Auth:
AuthState - Feed:
FeedViewModel,TweetCellViewModel,CreateTweetViewModel - Profile:
ProfileViewModel - Notifications:
NotificationsViewModel - Search:
SearchViewModel
- Auth:
-
视图
WelcomeView: 显示引导页面,提供"登录"和"创建账户"选项LoginView和RegisterView: 处理用户凭据输入,并调用AuthStateMainView: 包含标签视图(Feed、Search、Notifications、Messages)以及侧滑菜单(SlideMenu)ProfileView: 显示用户资料信息、推文,并允许编辑(如果是当前用户)EditProfileView: 允许编辑头像/横幅,修改名称、简介、位置、网站等SearchView: 列出用户,并按搜索文本过滤NotificationsView: 显示当前登录用户的通知
此应用程序使用自制的 DIContainer 类来注册和解析依赖项:
// 1. 注册
container.register(APIClient(baseURL: ...), type: .apiClient)
// 2. 解析
let apiClient: APIClientProtocol = container.resolve(.apiClient) ?? ...每个 View 或 ViewModel 需要服务时可以通过两种方式访问它:
@Environment(\.diContainer): 在SwiftUI视图中,检索容器并执行container.resolve(.authService)- 状态或环境注入: 例如,
TwitterCloneApp在启动时设置容器
-
用户启动应用程序
TwitterCloneApp检查UserDefaults中是否存储了JWT。如果存在,则调用authService.fetchCurrentUser()恢复会话
-
认证
- 用户点击 登录 →
AuthState.login(email, password)。成功后,接收 token,并将其存储在UserDefaults中 - 如果是 注册,类似地设置当前用户在
AuthState中
- 用户点击 登录 →
-
MainView 和 Feed
- 显示 TabView(主页、搜索、通知、消息)
FeedViewModel调用tweetService.fetchTweets()TweetCellViewModel处理点赞/取消点赞
-
个人资料
ProfileViewModel.fetchProfile()+fetchUserTweets()- 编辑通过
profileService.updateProfile()完成 - 头像/横幅通过
profileService.uploadAvatar()/uploadBanner()上传
-
通知
NotificationsViewModel.fetchNotifications()使用notificationService显示新的点赞/关注- 点赞时:
tweetService还会调用notificationService.createNotification(...)
-
搜索
SearchViewModel加载所有用户,用户列表按searchText过滤
login(email:password:)→APIResponseregister(...)→UserfetchCurrentUser()→UserupdateProfile(data:)→User
fetchTweets()→[Tweet]createTweet(text:userId:)→TweetlikeTweet(tweetId:)/unlikeTweet(tweetId:)→TweetuploadImage(tweetId:image:)→ImageUploadResponse
fetchUserProfile(userId:)→UserupdateProfile(data:)→UserfetchUserTweets(userId:)→[Tweet]uploadAvatar(imageData:)/uploadBanner(imageData:)→User
fetchNotifications(userId:)→[Notification]createNotification(username:receiverId:type:postText:)→Notification
- 作为一个新用户,我希望通过电子邮件、用户名和密码注册来创建一个账户
- 作为一个返回用户,我希望能够登录,以便查看我的主页信息流
- 作为一个认证用户,我希望发布新的推文(可附带图片)
- 作为一个认证用户,我希望通过点击点赞来表达对推文的喜爱
- 作为一个认证用户,我希望编辑我的个人资料(头像、横幅、姓名、简介、位置、网站)
- 作为一个认证用户,我希望在通知中查看哪些用户喜欢了我的推文
- 作为一个认证用户,我希望在搜索标签中看到其他用户的列表
- 作为一个用户,我希望查看他人的个人资料页面和推文
- 作为一个用户,我希望随时注销,以结束我的会话
本项目引用了位于 http://localhost:3000 的API。您可以构建自己的 Node/Express 或其他服务器来实现这些端点:
POST /users→ 注册POST /users/login→ 登录GET /users/me→ 当前用户PATCH /users/me→ 更新资料POST /users/me/avatar→ 上传头像POST /users/me/banner→ 上传横幅GET /tweets→ 获取推文POST /tweets→ 创建推文PUT /tweets/:id/like→ 点赞推文PUT /tweets/:id/unlike→ 取消点赞推文POST /tweets/:id/image→ 上传推文图片GET /notifications/:userId→ 获取通知POST /notifications→ 创建通知
对于图像字段,API必须解析multipart/form-data。请参阅 ImageUploader 和 APIClient.sendRequestWithoutDecoding(...) 以获取示例。
本项目是"按原样"提供的,不附带任何类型的保证。您可以自由地派生、修改和重新分发。感激但不是必须的给出署名。
愉快编码! 如果您有任何问题或需要进一步的帮助,请开设问题或提交拉取请求。