skip to Main Content

I wanted to make multiple API call in the same screen, but when one api fails other api should not be called? The below code is working fine. but what I need is , how can I refactor this in a more simpler way?

      ApplicationService.requestAppEndPointUrl { success, error in
        if success {
            ApplicationService.appLinkDownload { success, error in
                if success{
                    ApplicationService.requestApplicationSession { success, error in
                        if success {
                            ApplicationService.validateSdk { success, error in
                                if success {
                                    ApplicationService.requestApplicationDetails { success, error in
                                        if success{
                                            print("Success")
                                        }
                                        else{
                                            self.showErrorAlert(error)
                                        }
                                    }
                                }else{
                                    self.showErrorAlert(error)
                                }
                            }
                        }else{
                            self.showErrorAlert(error)
                        }
                    }
                }else{
                    self.showErrorAlert(error)
                }
            }
        }else{
            self.showErrorAlert(error)
        }
    }

3

Answers


  1. Thats a pretty question. So if you don’t want to pass data from one api call to the other the solution is pretty easy.

    All you have to do is to make a Service Manager that will manage all the service calls.

    You can make an enum based on the services you want to call

    enum ServiceEnum {
       case requestAppEndPointUrl
       case appLinkDownload
       case requestApplicationSession
       case validateSdk
       case requestApplicationDetails
    }
    
    enum ServiceState {
       case notStarted
       case inProgress
       case finished
       case failed
    }
    

    Then you can make a class of the service

    class Service {
       var service: ServiceEnum
       var serviceState: ServiceState = .notStarted
     
       init(service: ServiceEnum) {
         self.service = service
       }
    
       func updateState(serviceState: ServiceState) {
         self.serviceState = serviceState
       }
    }
    

    After that you will need to make the Service Manager

    class ServiceManager {
       private var services = [Services]()
       private var indexForService: Int {
          didSet {
              self.startServices()
          }
       }
       init() {
          services.append(.requestAppEndPointUrl)
          services.append(.appLinkDownload)
          services.append(.requestApplicationSession)
          services.append(.validateSdk)
          services.append(.requestApplicationDetails)
          indexForService = 0
       }
    
       func startServices() {
          // Check if the process has ended
          guard indexForService < services.count else {
              // If you want you can call an event here.
              // All the services have been completed
              return 
          }
          
          // Check that we have services to call
          guard services.count > 0 else {
              return
          }
    
          handleService(service: services[indexForService])
       }
    
       func haveAllFinished() -> Bool {
           return services.fist(where: { $0.serviceState == .failed || $0.serviceState == .notStarted || $0.serviceState == .inProgress }) == nil
       }
       
       func handleService(service: Service) -> Bool {
         switch(service) {
             case .requestAppEndPointUrl:
              ApplicationService.requestAppEndPointUrl { success, error in
                     let serviceState = success ? .finished : .failed
                     service.updateState(serviceState: serviceState)
                     if success {
                        self.indexForService += 1
                     } else {
                        self.showErrorAlert(error)
                     }
                 }
             // Do the same for other services
          }
       }
    } 
    
    Login or Signup to reply.
  2. for this, you need to use OperationQueue & ** DispatchGroup** where you can make your API calls in BlockOperation and one operation depends on another, and their dispatch group helps you to hold the API calls.

    func apiCall() {
        let dispatchGroup = DispatchGroup()
        let queue = OperationQueue()
        
        
        let operation1 = BlockOperation {
            dispatchGroup.enter()
            ApplicationService.requestAppEndPointUrl {
                Thread.sleep(forTimeInterval: Double.random(in: 1...3))
                print("ApplicationService.requestAppEndPointUrl()")
                dispatchGroup.leave()
            }
            dispatchGroup.wait()
        }
        
        let operation2 = BlockOperation {
            
            dispatchGroup.enter()
            ApplicationService.appLinkDownload {
                print("ApplicationService.appLinkDownload()")
                dispatchGroup.leave()
            }
            dispatchGroup.wait()
        }
        
        operation2.addDependency(operation1)
        
        let operation3 = BlockOperation {
            dispatchGroup.enter()
            ApplicationService.requestApplicationSession {
                Thread.sleep(forTimeInterval: Double.random(in: 1...3))
                print("ApplicationService.requestApplicationSession()")
                dispatchGroup.leave()
            }
            dispatchGroup.wait()
        }
        operation3.addDependency(operation2)
        
        let operation4 = BlockOperation {
            dispatchGroup.enter()
            ApplicationService.validateSdk {
                print("ApplicationService.validateSdk()")
                dispatchGroup.leave()
            }
            dispatchGroup.wait()
        }
        
        operation4.addDependency(operation3)
        
        let operation5 = BlockOperation {
            dispatchGroup.enter()
            ApplicationService.requestApplicationDetails {
                Thread.sleep(forTimeInterval: Double.random(in: 1...3))
                print("ApplicationService.requestApplicationDetails()")
                dispatchGroup.leave()
            }
            dispatchGroup.wait()
        }
        operation5.addDependency(operation4)
        
        queue.addOperations([operation1, operation2, operation3, operation4, operation5], waitUntilFinished: true)
    }
    

    I know code is a little bit nasty.

    and if you are using swift 5.5 then use Async await mentioned by @Reinhard Männer

    Login or Signup to reply.
  3. If ApplicationService is a class/struct that you can modify, you could convert the synchronous function calls with completion handler to asynchronous function calls, using Swift 5.5 concurrency. The code would look like:

    do {
      try await ApplicationService.requestAppEndPointUrl()
      try await ApplicationService.appLinkDownload()
      try await ApplicationService.requestApplicationSession()
      try await ApplicationService.validateSdk()
      try await ApplicationService.requestApplicationDetails()
    } catch {
      self.showErrorAlert(error)
    }
    

    Then, the 1st error would break the call chain, would be cought and shown.
    If you had to use the synchronous ApplicationService functions, you could convert them to asynchronous functions by using async wrappers as shown in the link cited above.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search