Skip to content

Latest commit

 

History

History
186 lines (154 loc) · 5.49 KB

File metadata and controls

186 lines (154 loc) · 5.49 KB

SwiftUI Generic Service with Alamofire and URLSession

Swift Platform Platform License

This example project demonstrates Dependency Injection (DI) and Singleton design patterns using SwiftUI, along with creating a Generic Network Manager with MVVM.

The project aims to fetch data from a DummyJSON API using Alamofire or URLSession.

  • Desing Patterns
  • MVVM
  • Alamofire Api Service
  • URLSession Api Service
  • Auth
  • Generic Network Layer
  • UI Design
  • 3rd libraries

Product Model

  struct ProductResponse: Codable {
    let products: [Product]
}

struct Product: Codable, Identifiable {
    let id: Int?
    let title, description: String
    let price: Int
    let discountPercentage, rating: Double
    let stock: Int
    let brand, category: String
    let thumbnail: String
    let images: [String]
}

ApiServiceProtocol Protocol

The protocol named ApiServiceProtocol defines the necessary functionality for making network calls.

protocol ApiServiceProtocol {
    func getRequest<T: Codable>(endpoint: URL, parameters: [String: Any]?, completion: @escaping(Result<T, Error>) -> Void )
    func addRequest<T: Codable>(endpoint: URL, data: T, completion: @escaping(Result<Void, Error>) -> Void)
    func updateRequest<T: Codable>(endpoint: URL, data: T, completion: @escaping(Result<Void, Error>) -> Void)
    func deleteRequest(endpoint: URL, completion: @escaping(Result<Void, Error>) -> Void)
}

AlamofireApiService

The class named AlamofireApiService implements the ApiService protocol and designs this class as a singleton.

class AlamofireApiService: ApiServiceProtocol {
    
    private init() { }
    
    static let shared = AlamofireApiService()
    
    func getRequest<T: Codable>(endpoint: URL, parameters: [String: Any]?, completion: @escaping (Result<T, Error>) -> Void) {
        AF.request(endpoint, method: .get, parameters: parameters)
        .validate()
        .responseDecodable(of: T.self) { response in
            switch response.result {
            case .success(let value):
                completion(.success(value))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }

    // postRequest()
    // updateReques()
    // deleteRequest()

ProductApiService

The class named ProductApiService performs network operations with the API service defined through Dependency Injection (DI), which is an instance implementing the ApiService protocol.

class ProductApiService{
    
    let apiService: ApiServiceProtocol
    
    init(apiService: ApiServiceProtocol) {
        self.apiService = apiService
    }
    
    func getAllProducts(parameters: [String: Any]?, completion: @escaping (Result<[Product], Error>) -> Void ){ // tüm postları getirmek için
        apiService.getRequest(endpoint: APIConstants.getProductEndpoint, parameters: parameters) { (result: Result<ProductResponse, Error>) in
            switch result {
            case .success(let data):
                completion(.success(data.products))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}
// getProductById()
// createProduct()
// updateProduct()
// deleteProduct()

ProductViewModel

The ProductViewModel is used to fetch products and update the product interface. It injects the ApiServiceProtocol using Dependency Injection.

class ProductViewModel: ObservableObject {
    
    @Published var products: [Product] = []
    @Published var product: Product?
    @Published var isShowing: Bool = false
    @Published var isError: Bool = false
    @Published var title = ""
    @Published var message = ""
    
    let productApiService: ProductApiService
    
    init(productApiService: ProductApiService) {
        self.productApiService = productApiService
    }
    
    func fetchAllProducts(parameters: [String: Any]? = nil) {
        productApiService.getAllProducts(parameters: parameters) { result in
            switch result {
            case .success(let products):
                DispatchQueue.main.async {
                    self.products = products
                }
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }
}

// fetchProductById()
// addProduct()
// updateProduct()
// deleteProduct()

View ile Kullanımı

The ApiServiceProtocol is injected into ProductViewModel as a singleton using Dependency Injection, and this ViewModel is used by the ProductListView.

struct ProductListView: View {
    
    @StateObject var viewModel: ProductViewModel
    init(apiService:  ApiServiceProtocol) {
        _viewModel = StateObject(wrappedValue: ProductViewModel(productApiService: .init(apiService: apiService)))
    }

    var body: some View {
        // ...
    }
}

struct ContentView: View {
    var body: some View {
        NavigationStack {
            VStack {
                ProductListView(apiService: AlamofireApiService.shared)
            }
            .navigationTitle("Products")
        }
    }
}

Özellikler

  • MVVM
  • Singleton
  • Dependency Injection
  • Generic Network Layer
  • Protocol Oriented Programming (POP)
  • Alamofire & URLSession
  • ContentUnavailableView
  • SDWebImage
  • SwipeActions
  • .searchable modifier
  • NavigationStack