İçeriğe geç

SwiftUI ile Combine Request ve Error Handling

Merhabalar bu yazımda SwiftUI ile birlikte sizlere Combine kullanarak request ve error handling işlemini nasıl gerçekleştirebileceğinizi yazdığım örnek ile göstereceğim.

Combine ile bildiğiniz gibi RxSwift‘e rakip Apple şirketinin geliştirdiği işlevsel reaktif programlamanın yerel Swift uygulamasıdır. Bu yapı ile bir birine bağlı zincirleme fonksiyonlar çağırarak işlemler gerçekleştirebilir ve main thread üzerinde yürüyen request işlemleri gerçekleştirip, error handling yapabilirsiniz. Tabiki Combine ile yapabileceklerinizde sınır yok. Aklınıza gelecek bir çok işlemi bu yapıya uydurup, daha modern ve arayüz ile bütün şekilde çalışan uygulamalar geliştirebilirsiniz.

Aşağıda yazdığım örnek ile basit bir şekilde free Rest API kullanarak bir liste fetch edeceğiz ve SwiftUI ile combine yapısına adapte bir error handling yazacağız.

İlk olarak request işlemini gerçekleştireceğimiz bir class yazalım.

final class TodosService {
    
    private let session: URLSession
    
    init(session: URLSession = .shared) {
        self.session = session
    }
    
    func getTodos() -> AnyPublisher<[TodosModel], FailureReason> {
        let request = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos")!)        
        return session.dataTaskPublisher(for:  request)
            .map { $0.data }
            .decode(type: [TodosModel].self, decoder: JSONDecoder())
            .mapError({ error in
                switch error {
                case is Swift.DecodingError: return .decodingFailed
                case let urlError as URLError: return .sessionFailed(error: urlError)
                default: return .other(error)
                }
            })
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}
struct TodosModel: Codable, Identifiable, Hashable {
    var userId: Int
    var id: Int
    var title: String = ""
    var completed: Bool = false
}

İlgili View nesnemiz için bir View Model oluşturup burada gerekli bağlama ve TodosService fetch işlemini gerçekleştirelim.

class ContentViewModel: ObservableObject {
    
    @Published private(set) var todos: [TodosModel] = []
    @Published var errorItem: ErrorItem?
    
    private var cancellable: AnyCancellable?
    private let service = TodosService()
    
    func getItems() {
        cancellable = service.getTodos()
            .sink { error in
                switch error {
                case .finished:
                    break
                case .failure(let error):
                    self.errorItem = ErrorItem(errorItem: error)
                }
            } receiveValue: { items in
                self.todos = items
            }
    }
}
struct ErrorItem: Identifiable {
    var id: String = UUID().uuidString
    var errorItem: FailureReason
}

extension ErrorItem {
    var message: String {
        switch errorItem {
        case .decodingFailed: return "Decoding Error"
        case .sessionFailed(error: let uError): return "Url Error: \(uError.localizedDescription)"
        case .other(let error): return "Problem \(error.localizedDescription)"
        }
    }
}

enum FailureReason : Error {
    case sessionFailed(error: URLError)
    case decodingFailed
    case other(Error)
}

Arayüzümüzü oluşturup gerekli View Model bağlama işlemini yapabiliriz.

struct ContentView: View {
    
    @StateObject var VM = ContentViewModel()
    
    @State var alertError: ErrorItem?
    
    var body: some View {
        ZStack {
            
            if VM.todos.isEmpty {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            }
            
            Spacer().alert(item: $VM.errorItem) { errorItem in
                Alert(title: Text("Oops"), message: Text(errorItem.message), dismissButton: nil)
            }
            
            NavigationView {
                List {
                    ForEach(VM.todos, id: \.self) { item in
                        RowView(item: item)
                            .padding()
                    }
                }
                .onAppear {
                    VM.getItems()
                }
                .navigationTitle("Todos List")
            }
        }
    }
}

 

Kategori:CombineSwiftUI

İlk Yorumu Siz Yapın

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

Copyright © 2022 Kenan Atmaca