Protocols/面向协议编程, DependencyInjection/依赖式注入 的使用

1. Protocols 定义实现协议,面向协议编码

1.1 创建面向协议实例 ProtocolsBootcamp.swift

Swift 复制代码
import SwiftUI

/// 颜色样式协议
protocol ColorThemeProtocol {
    var primary: Color { get }
    var secondary: Color { get }
    var tertiary: Color { get }
}

struct DefaultColorTheme: ColorThemeProtocol {
    let primary: Color   = .blue
    let secondary: Color = .white
    let tertiary: Color  = .gray
}

struct AlternativeColorTheme: ColorThemeProtocol {
    let primary: Color   = .red
    let secondary: Color = .white
    let tertiary: Color  = .orange
}

struct AnotherColorTheme: ColorThemeProtocol{
    var primary: Color  = .blue
    var secondary: Color = .red
    var tertiary: Color  = .purple
}

/// 定义按钮文字协议
protocol ButtonTextProtocol{
    var buttonText: String { get }
}

protocol ButtonPressedProtocol{
    func buttonPressed()
}

protocol ButtonDataSourceProtocol: ButtonTextProtocol, ButtonPressedProtocol{
}

class DefaultDataSource: ButtonDataSourceProtocol{
    var buttonText: String = "Protocols are awesome!"
    
    func buttonPressed(){
        print("Button was pressed!")
    }
}

class AlternativeDataSource: ButtonTextProtocol{
    var buttonText: String = "Protocols are lame."
}

/// 面向协议
struct ProtocolsBootcamp: View {
    let colorTheme: ColorThemeProtocol
    let dataSource: ButtonDataSourceProtocol
    
    var body: some View {
        ZStack {
            colorTheme.tertiary
                .ignoresSafeArea()
            Text(dataSource.buttonText)
                .font(.headline)
                .foregroundColor(colorTheme.secondary)
                .padding()
                .background(colorTheme.primary)
                .cornerRadius(10)
                .onTapGesture {
                    dataSource.buttonPressed()
                }
        }
    }
}

struct ProtocolsBootcamp_Previews: PreviewProvider {
    static var previews: some View {
        // DefaultColorTheme / AlternativeColorTheme / AnotherColorTheme
        ProtocolsBootcamp(colorTheme: DefaultColorTheme(), dataSource: DefaultDataSource())
    }
}

1.2 效果图:

2. DependencyInjection 依赖式注入

2.1 创建依赖式注入的实例 DependencyInjectionBootcamp.swift

Swift 复制代码
import SwiftUI
import Combine

// Problems with singletons
// 1. Singleton's are GLOBAL      单例模式是全局的
// 2. Can't customize the init!   不能自定义初始化
// 3. Can't swap out dependencies 不能交换式依赖
struct PostsMode: Identifiable, Codable{
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

/// 定义协议 数据服务
protocol DataServiceProtocol {
    /// 获取数据
    func getData() -> AnyPublisher<[PostsMode], Error>
}

/// 生产者数据服务
class ProductionDataService: DataServiceProtocol{
    /// 单例 Singleton
   // static let instance = ProductionDataService()
    
    let url: URL
    
    init(url: URL) {
        self.url = url
    }
    
    func getData() -> AnyPublisher<[PostsMode], Error>{
        URLSession.shared.dataTaskPublisher(for: url)
            .map({$0.data})
            .decode(type: [PostsMode].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}

/// 模拟请求服务器返回数据
class MockDataService: DataServiceProtocol{
    let testData: [PostsMode]
    
    init(data: [PostsMode]? ) {
        self.testData = data ?? [
            PostsMode(userId: 1, id: 1, title: "One", body: "One"),
            PostsMode(userId: 2, id: 2, title: "Two", body: "Two"),
            PostsMode(userId: 3, id: 3, title: "Three", body: "Three")
        ]
    }
    
    func getData() -> AnyPublisher<[PostsMode], Error> {
        Just(testData)
            .tryMap({ $0 })
            .eraseToAnyPublisher()
    }
}

/// 依赖试
class Dependencies {
    let dataService: DataServiceProtocol
    
    init(dataService: DataServiceProtocol) {
        self.dataService = dataService
    }
}

/// ViewModel
class DependencyInjectionViewModel: ObservableObject{
    @Published var dataArray: [PostsMode] = []
    var cancellables =  Set<AnyCancellable>()
    let dataService: DataServiceProtocol
    
    init(dataService: DataServiceProtocol) {
        self.dataService = dataService
        loadPosts()
    }

    private func loadPosts(){
        dataService.getData()
            .sink { _ in
                
            } receiveValue: {[weak self] returnedPosts in
                self?.dataArray = returnedPosts
            }
            .store(in: &cancellables)
    }
}

/// 依赖注入
struct DependencyInjectionBootcamp: View {
    @StateObject private var vm: DependencyInjectionViewModel
    
    init(dataService: DataServiceProtocol){
        _vm = StateObject(wrappedValue: DependencyInjectionViewModel(dataService: dataService))
    }
    
    var body: some View {
        ScrollView {
            VStack {
                ForEach(vm.dataArray) { post in
                    Text(post.title)
                    Divider()
                }
            }
        }
    }
}

struct DependencyInjectionBootcamp_Previews: PreviewProvider {
    // static let dataService = ProductionDataService(url: URL(string: "https://jsonplaceholder.typicode.com/posts")!)
    static let dataService = MockDataService(data: [
        PostsMode(userId: 12, id: 12, title: "test", body: "test"),
        PostsMode(userId: 123, id: 123, title: "123", body: "123")
    ])
    
    static var previews: some View {
        DependencyInjectionBootcamp(dataService: dataService)
    }
}

2.2 效果图:

相关推荐
用户468104557243 小时前
Swift 异步与并发
swift
Anhty4 小时前
叮咚变声器上手实测,iOS短板、安卓悬浮窗使用感受分享
android·人工智能·功能测试·ios·智能手机
EatFan6 小时前
【实战经验】uni-app使用 SSE 踩坑,EventSource不支持怎么办?
android·后端·ios·uni-app
黑科技iOS上架6 小时前
iOS市场截图尺寸规范的生成器
ios·审核
逻辑克7 小时前
iPhone Duo 适配 Drag and Drop 实现方案
ios·程序员
可乐鸡翅yeah_8 小时前
HLS CORS 跨域问题完整排查实战,解决 M3U8 与 TS 分片跨域报错
android·ios·音视频·m3u8·音视频在线播放
黑科技iOS上架8 小时前
iOS预审工具相似度检测篇
ios·审核
安当加密03018 小时前
PCI DSS 4.0与SWIFT CSP双合规:银行收单与跨境支付HSM部署实战
swift·hsm·密钥管理·pci dss·双合规
光影少年9 小时前
React18 对RN 的影响
android·前端·react.js·ios·前端框架
ZJPRENO10 小时前
2026 苹果秋季新品发布会总结
ios