以下是针对您项目特点的优化方案,保持原有功能的同时实现更好的架构:
AppDelegate.swift
import UIKit
import Toast_Swift
import IQKeyboardManagerSwift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private lazy var appCoordinator = AppCoordinator()
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 1. 基础配置
configureAppEssentials()
// 2. 初始化主窗口
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
// 3. 启动主流程
appCoordinator.start(launchOptions: launchOptions, in: window)
return true
}
private func configureAppEssentials() {
// 三方库基础配置
IQKeyboardManager.shared.enable = true
LibsManager.shared.setupLibs()
// Toast 样式配置
var toastStyle = ToastManager.shared.style
toastStyle.backgroundColor = UIColor.Material.green
ToastManager.shared.style = toastStyle
}
}
AppCoordinator.swift
(核心协调器)import RxSwift
import RxCocoa
final class AppCoordinator {
private let disposeBag = DisposeBag()
private let authManager = AuthManager.shared
private let navigator = Navigator.default
private var provider: SwiftHubAPI?
func start(launchOptions: [UIApplication.LaunchOptionsKey: Any]?, in window: UIWindow?) {
// 1. 环境配置
configureEnvironment()
// 2. 恢复用户会话
restoreUserSession()
// 3. 设置主题
applyTheme()
// 4. 初始化网络监测
setupNetworkMonitoring()
// 5. 显示初始界面
presentInitialScreen(in: window)
}
private func configureEnvironment() {
let staging = Configs.Network.useStaging
if staging {
// 测试环境特殊配置
applyTestEnvironmentSettings()
}
}
private func applyTestEnvironmentSettings() {
// 登出用户
User.removeCurrentUser()
AuthManager.removeToken()
// 使用绿色深色主题
var theme = ThemeType.currentTheme()
if !theme.isDark { theme = theme.toggled() }
theme = theme.withColor(color: .green)
themeService.switch(theme)
// 禁用横幅
LibsManager.shared.bannersEnabled.accept(false)
}
private func restoreUserSession() {
// 更新API提供者
updateProvider()
}
private func updateProvider() {
let staging = Configs.Network.useStaging
let githubProvider = staging ? GithubNetworking.stubbingNetworking() : GithubNetworking.defaultNetworking()
let trendingGithubProvider = staging ? TrendingGithubNetworking.stubbingNetworking() : TrendingGithubNetworking.defaultNetworking()
let codetabsProvider = staging ? CodetabsNetworking.stubbingNetworking() : CodetabsNetworking.defaultNetworking()
let restApi = RestApi(
githubProvider: githubProvider,
trendingGithubProvider: trendingGithubProvider,
codetabsProvider: codetabsProvider
)
provider = restApi
if let token = authManager.token, !staging {
switch token.type() {
case .oAuth(let token), .personal(let token):
provider = GraphApi(restApi: restApi, token: token)
default: break
}
}
}
private func applyTheme() {
// 主题初始化逻辑
themeService.switch(ThemeType.currentTheme())
}
private func setupNetworkMonitoring() {
// 仅在生产环境监测网络
guard !Configs.Network.useStaging else { return }
connectedToInternet()
.skip(1)
.subscribe(onNext: { [weak self] connected in
self?.showNetworkStatusToast(connected: connected)
})
.disposed(by: disposeBag)
}
private func showNetworkStatusToast(connected: Bool) {
var style = ToastManager.shared.style
style.backgroundColor = connected ? UIColor.Material.green : UIColor.Material.red
let message = connected ?
R.string.localizable.toastConnectionBackMessage.key.localized() :
R.string.localizable.toastConnectionLostMessage.key.localized()
let image = connected ?
R.image.icon_toast_success() :
R.image.icon_toast_warning()
if let view = UIApplication.shared.keyWindow?.rootViewController?.view {
view.makeToast(message, position: .bottom, image: image, style: style)
}
}
private func presentInitialScreen(in window: UIWindow?) {
guard let window = window, let provider = provider else { return }
// 延迟0.5秒确保UI就绪
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
self?.setupAnalytics()
let authorized = self?.authManager.token?.isValid ?? false
let viewModel = HomeTabBarViewModel(authorized: authorized, provider: provider)
self?.navigator.show(
segue: .tabs(viewModel: viewModel),
sender: nil,
transition: .root(in: window)
}
}
private func setupAnalytics() {
guard let user = User.currentUser(), let login = user.login else { return }
analytics.identify(userId: login)
if let name = user.name { analytics.set(.name(value: name)) }
if let email = user.email { analytics.set(.email(value: email)) }
}
}
Application.swift
import UIKit
final class Application {
static let shared = Application()
// 原始功能保持不变,但移除启动相关逻辑
var provider: SwiftHubAPI?
let authManager: AuthManager
let navigator: Navigator
private init() {
authManager = AuthManager.shared
navigator = Navigator.default
}
// 保留此方法供其他模块使用
func updateProvider() {
// 实现与AppCoordinator中的updateProvider类似
// 为保持一致性,可考虑将此逻辑移至共享服务
}
}
EnvironmentManager.swift
import Foundation
struct EnvironmentManager {
static var isStaging: Bool {
Configs.Network.useStaging
}
static func configureEnvironment() {
if isStaging {
// 测试环境配置
configureTestEnvironment()
}
}
private static func configureTestEnvironment() {
// 登出用户
User.removeCurrentUser()
AuthManager.removeToken()
// 配置主题
var theme = ThemeType.currentTheme()
if !theme.isDark { theme = theme.toggled() }
theme = theme.withColor(color: .green)
themeService.switch(theme)
// 禁用横幅
LibsManager.shared.bannersEnabled.accept(false)
}
}
职责清晰分离
AppDelegate
:仅处理应用生命周期AppCoordinator
:核心业务协调EnvironmentManager
:环境配置专用可测试性提升
class AppCoordinatorTests: XCTestCase {
func testStagingConfiguration() {
Configs.Network.useStaging = true
let coordinator = AppCoordinator()
coordinator.configureEnvironment()
XCTAssertNil(User.currentUser())
XCTAssertFalse(LibsManager.shared.bannersEnabled.value)
}
}
功能模块化
保留原有优势
Application
类的功能性LibsManager
的使用方式启动流程优化
维护成本降低
任务 | 重构前 | 重构后 |
---|---|---|
修改环境配置 | 需搜索多个文件 | 仅修改 EnvironmentManager |
添加新启动任务 | 需修改 AppDelegate |
在 AppCoordinator 添加方法 |
调试启动问题 | 需跟踪整个流程 | 单一入口调试 |
扩展性增强
EnvironmentManager
AppCoordinator
中添加方法presentInitialScreen
增量迁移
gantt
title 重构计划
dateFormat YYYY-MM-DD
section 第一阶段
创建AppCoordinator :done, des1, 2023-08-01, 3d
迁移环境配置 :active, des2, after des1, 2d
section 第二阶段
迁移网络监测 : des3, after des2, 2d
重构UI初始化 : des4, after des3, 2d
section 第三阶段
清理AppDelegate : des5, after des4, 1d
编写单元测试 : des6, after des5, 3d
兼容性保障
// 临时兼容方案
#if !REFACTORED
// 旧版启动代码
#else
// 新版启动代码
#endif
验证机制
// 添加启动日志验证
func start() {
log("启动流程开始")
configureEnvironment()
log("环境配置完成")
// ...
log("启动流程结束")
}
当前 Application 类承担了过多职责:
这违反了单一职责原则,导致:
final class Application {
// MARK: - 核心服务容器
static let shared = Application()
// 服务实例
let authManager: AuthManager
let navigator: Navigator
var apiProvider: SwiftHubAPI?
private init() {
// 初始化基础服务
authManager = AuthManager.shared
navigator = Navigator.default
// 配置API提供者
configureAPIProvider()
}
// MARK: - 服务配置
private func configureAPIProvider() {
let networkService = NetworkServiceFactory.create(
useStaging: EnvironmentManager.isStaging
)
apiProvider = networkService.apiProvider
// 认证令牌处理
if let token = authManager.token, !EnvironmentManager.isStaging {
apiProvider = AuthenticatedAPIDecorator(
baseProvider: networkService.apiProvider,
token: token
)
}
}
// MARK: - 服务更新
func refreshServices() {
configureAPIProvider()
}
}
struct NetworkServiceFactory {
static func create(useStaging: Bool) -> NetworkService {
if useStaging {
return StagingNetworkService()
} else {
return ProductionNetworkService()
}
}
}
protocol NetworkService {
var apiProvider: SwiftHubAPI { get }
}
struct ProductionNetworkService: NetworkService {
var apiProvider: SwiftHubAPI {
RestApi(
githubProvider: GithubNetworking.defaultNetworking(),
trendingGithubProvider: TrendingGithubNetworking.defaultNetworking(),
codetabsProvider: CodetabsNetworking.defaultNetworking()
)
}
}
struct StagingNetworkService: NetworkService {
var apiProvider: SwiftHubAPI {
RestApi(
githubProvider: GithubNetworking.stubbingNetworking(),
trendingGithubProvider: TrendingGithubNetworking.stubbingNetworking(),
codetabsProvider: CodetabsNetworking.stubbingNetworking()
)
}
}
struct AuthenticatedAPIDecorator: SwiftHubAPI {
private let baseProvider: SwiftHubAPI
private let token: String
init(baseProvider: SwiftHubAPI, token: String) {
self.baseProvider = baseProvider
self.token = token
}
func request(_ target: TargetType) -> Single<Response> {
// 添加认证逻辑
var authenticatedTarget = target
authenticatedTarget.headers?["Authorization"] = "Bearer \(token)"
return baseProvider.request(authenticatedTarget)
}
}
class EnvironmentManager {
static var isStaging: Bool {
Configs.Network.useStaging
}
static func configure() {
if isStaging {
configureStagingEnvironment()
}
}
private static func configureStagingEnvironment() {
// 清除用户数据
UserSessionManager.clear()
// 配置主题
ThemeManager.applyTestTheme()
// 禁用非核心功能
FeatureToggles.disable(.banners)
}
}
class UserSessionManager {
static var currentUser: User? {
return User.currentUser()
}
static func clear() {
User.removeCurrentUser()
AuthManager.removeToken()
}
static func restore() {
// 会话恢复逻辑
}
}
模块 | 职责 | 方法示例 |
---|---|---|
Application | 服务容器与访问点 | shared , apiProvider , refreshServices() |
EnvironmentManager | 环境配置管理 | configure() , isStaging |
NetworkServiceFactory | 网络服务创建 | create(useStaging:) |
AppCoordinator | 界面导航控制 | presentInitialScreen() , showTabBar() |
AnalyticsService | 分析跟踪 | identify(user:) , track(event:) |
UserSessionManager | 用户会话处理 | currentUser , clear() , restore() |
职责清晰化
可测试性提升
class ApplicationTests: XCTestCase {
func testAPIProviderConfiguration() {
// 给定
EnvironmentManager.isStaging = true
// 当
let application = Application()
// 则
XCTAssertTrue(application.apiProvider is StagingNetworkService)
}
}
扩展性增强
NetworkServiceFactory
维护成本降低
变更类型 | 重构前影响 | 重构后影响 |
---|---|---|
修改环境配置 | 多个文件 | 仅 EnvironmentManager |
添加新API | 修改 Application | 实现新 NetworkService |
更改导航逻辑 | 修改 Application | 修改 AppCoordinator |
第一阶段:创建基础设施
gantt
title 重构计划
dateFormat YYYY-MM-DD
section 核心架构
创建服务工厂 : done, 2023-08-01, 2d
实现环境管理器 : active, 2023-08-03, 2d
section 迁移逻辑
迁移API配置 : 2023-08-05, 3d
迁移用户会话 : 2023-08-08, 2d
第二阶段:逐步替换
// 临时兼容方案
extension Application {
@available(*, deprecated, message: "Use AppCoordinator instead")
func presentInitialScreen(in window: UIWindow?) {
AppCoordinator.shared.presentInitialScreen(in: window)
}
}
最终架构
重构后的 Application 类将专注于作为服务容器,为整个应用提供统一的服务访问点,同时将其他职责合理分配给专业模块,形成清晰、可维护的架构体系。