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") } } } }
İlk Yorumu Siz Yapın