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
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)
}
}