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

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

  1.1 创建面向协议实例 ProtocolsBootcamp.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 效果图:

Protocols/面向协议编程, DependencyInjection/依赖式注入 的使用_第1张图片

2. DependencyInjection 依赖式注入

  2.1 创建依赖式注入的实例 DependencyInjectionBootcamp.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()
    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 效果图:

Protocols/面向协议编程, DependencyInjection/依赖式注入 的使用_第2张图片

你可能感兴趣的:(SwiftUI,Advanced,Learning,iOS,Swift,UI)