AppDelegate 结构和 文件夹结构

具体代码重构方案

以下是针对您项目特点的优化方案,保持原有功能的同时实现更好的架构:

1. 重构后的 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
    }
}
2. 新增 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)) }
    }
}
3. 重构 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类似
        // 为保持一致性,可考虑将此逻辑移至共享服务
    }
}
4. 新增 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
AuthManager
ThemeManager
NetworkMonitor
RootViewController
配置测试环境
恢复用户会话
应用主题
网络状态监测
显示主界面
  1. 职责清晰分离

    • AppDelegate:仅处理应用生命周期
    • AppCoordinator:核心业务协调
    • EnvironmentManager:环境配置专用
  2. 可测试性提升

    class AppCoordinatorTests: XCTestCase {
        func testStagingConfiguration() {
            Configs.Network.useStaging = true
            let coordinator = AppCoordinator()
            
            coordinator.configureEnvironment()
            
            XCTAssertNil(User.currentUser())
            XCTAssertFalse(LibsManager.shared.bannersEnabled.value)
        }
    }
    
  3. 功能模块化

  4. 保留原有优势

    • 保持 Application 类的功能性
    • 兼容现有 LibsManager 的使用方式
    • 保留 RxSwift 的响应式网络监测

重构收益分析

  1. 启动流程优化

    App AppDelegate AppCoordinator EnvironmentManager AuthManager ThemeManager NetworkMonitor RootVC didFinishLaunching start() configure() restoreSession() applyTheme() start() present() App AppDelegate AppCoordinator EnvironmentManager AuthManager ThemeManager NetworkMonitor RootVC
  2. 维护成本降低

    任务 重构前 重构后
    修改环境配置 需搜索多个文件 仅修改 EnvironmentManager
    添加新启动任务 需修改 AppDelegate AppCoordinator 添加方法
    调试启动问题 需跟踪整个流程 单一入口调试
  3. 扩展性增强

    • 添加新环境(如预发布环境)只需扩展 EnvironmentManager
    • 新增启动任务只需在 AppCoordinator 中添加方法
    • 替换导航系统只需重写 presentInitialScreen

迁移步骤建议

  1. 增量迁移

    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
    
  2. 兼容性保障

    // 临时兼容方案
    #if !REFACTORED
    // 旧版启动代码
    #else
    // 新版启动代码
    #endif
    
  3. 验证机制

    // 添加启动日志验证
    func start() {
        log("启动流程开始")
        configureEnvironment()
        log("环境配置完成")
        // ...
        log("启动流程结束")
    }
    

Application 类职责分析与重构方案

当前 Application 类的职责分析
Application
服务管理
界面导航
环境配置
用户管理
API提供者
网络服务配置
根视图控制
导航器管理
环境判断
测试环境特殊处理
用户会话管理
用户识别

当前 Application 类承担了过多职责:

  1. 服务提供者:创建和管理 API 提供者
  2. 环境配置:处理 staging 环境特殊逻辑
  3. 用户管理:处理用户认证状态
  4. 界面导航:控制根视图显示
  5. 分析跟踪:用户识别和属性设置

这违反了单一职责原则,导致:

  • 类膨胀(200+ 行代码)
  • 职责边界模糊
  • 难以独立测试
  • 修改环境配置会影响界面逻辑
重构后的职责划分方案
Application
服务容器
API提供者
认证管理
EnvironmentManager
环境配置
测试环境设置
AppCoordinator
界面导航
根视图控制
AnalyticsService
用户识别
属性跟踪
NetworkService
API客户端
环境适配
1. 重构后的 Application.swift
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()
    }
}
2. 新增服务工厂
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()
        )
    }
}
3. 认证API装饰器
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)
    }
}
4. 环境管理器
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)
    }
}
5. 用户会话管理器
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()

重构优势分析

  1. 职责清晰化

  2. 可测试性提升

    class ApplicationTests: XCTestCase {
        func testAPIProviderConfiguration() {
            // 给定
            EnvironmentManager.isStaging = true
            
            // 当
            let application = Application()
            
            // 则
            XCTAssertTrue(application.apiProvider is StagingNetworkService)
        }
    }
    
  3. 扩展性增强

    • 添加新环境只需扩展 NetworkServiceFactory
    • 新增认证方式只需实现新装饰器
    • 修改界面导航不影响核心服务
  4. 维护成本降低

    变更类型 重构前影响 重构后影响
    修改环境配置 多个文件 仅 EnvironmentManager
    添加新API 修改 Application 实现新 NetworkService
    更改导航逻辑 修改 Application 修改 AppCoordinator

迁移路径建议

  1. 第一阶段:创建基础设施

    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
    
  2. 第二阶段:逐步替换

    // 临时兼容方案
    extension Application {
        @available(*, deprecated, message: "Use AppCoordinator instead")
        func presentInitialScreen(in window: UIWindow?) {
            AppCoordinator.shared.presentInitialScreen(in: window)
        }
    }
    
  3. 最终架构

    AppDelegate
    AppCoordinator
    Application
    NetworkService
    AuthManager
    EnvironmentManager
    AnalyticsService
    RootViewController

重构后的 Application 类将专注于作为服务容器,为整个应用提供统一的服务访问点,同时将其他职责合理分配给专业模块,形成清晰、可维护的架构体系。

你可能感兴趣的:(AppDelegate 结构和 文件夹结构)