2

If anyone can think of another way to achieve what I'm trying to do, your thoughts are welcome!

To give you an idea, I have over 40 different functions that have signatures similar to these:

func getXFromServer(arg1: String, arg2: Int, arg3: Bool, completion: ((MyCustomResponse)->Void)? = nil)
func getYDataFromServer(completion: @escaping (Bool)->Void)
func getZDataFromServer(arg1: Int, completion: @escaping (MyGeneric<MyClass>)->Bool)

Some have arguments, some don't; some have a completion handler, and some are optional. All sorts of Types are passed around.

I'm trying to streamline the process of handling the server's response: currently it has a lot of repeated code, all doing pretty much the same thing, just with different argument values and types.

Because these functions are all asynchronous, I use completion handlers to handle the server data when it's retrieved. I want all the 'repeated code' to go in one function, let's call it handleServerResponse: depending on the server response's value, I want to do more async stuff and be able to re-execute the same outer function

For example:

func getYDataFromServer(completion: @escaping (Bool)->Void)
{
    session.dataTask(with: URLRequest()) { data, response, error in
        handleServerResponse(){ valid   //I want to pass 'getYDataFromServer(completion: completion)' here so that 'handleServerResponse' can determine whether to re-execute it or not, based on the server response

            //do more stuff with Y server data
        }
    }.resume()
}

I am currently handling the re-execution outside of handleServerResponse, like so:

func getYDataFromServer(completion: @escaping (Bool)->Void)
{
    session.dataTask(with: URLRequest()) { data, response, error in
        handleServerResponse(){ valid
            if !valid
            {
                self.getXYZDataFromServer(){
                    getYDataFromServer(completion: completion)
                }
                return
            }
            //do more stuff with Y server data
        }
    }.resume()
}

This is a very simplistic version of the code, but as you can see it gets to a lot of repeated code very fast.

Thanks

Merricat
  • 2,583
  • 1
  • 19
  • 27

1 Answers1

0

Maybe this is helpful

  func getData(_ args: Any..., completion: @escaping ((Any)->Any)){
print(args)
completion(11)
  }

But the function type cannot coercive to Any, you may consider to use enum to mix for safety types.

            var valid: Bool = false
            func getXFromServer(arg1: String, arg2: Int, arg3: Bool, completion: ((String)->Void)? = nil){}

            func getYDataFromServer(completion: @escaping (Bool)->Void){
              //  session.dataTask(with: URLRequest()) { data, response, error in
                //    handleServerResponse(){ valid
                     valid.toggle()
                     print(valid)
                        if  valid
                        {
                            MixGetData.XYZ.run{ MixCompletion.XYZ{ firstY.run{MixCompletion.Y(completion) } }}

                        }
                            else {
                                completion(false)
                            }
                   // }
                  //  }.resume()

            }

            func getZDataFromServer(arg1: Int, completion: @escaping (String)->Bool){}
            func getXYZDataFromServer(completion: @escaping ()->Void){
                completion()
            }

            enum MixCompletion{
                case X(((String)->Void)?)
                case Y((Bool)->Void)
                case Z((String)->Bool)
                case XYZ(()->Void)
            }

            enum MixGetData{
                case X( String, Int, Bool )
                case Y
                case Z(Int)
                case XYZ

                func run(completion: (() -> MixCompletion)? = nil){
                    if (completion == nil) {
                    switch (self) {
                    case let .X(arg1, arg2, arg3) : getXFromServer(arg1: arg1, arg2: arg2, arg3: arg3, completion : nil)
                    case let .Z(arg1) : getZDataFromServer(arg1: arg1, completion: {_ in return false})
                    case .Y : getYDataFromServer(completion: {_ in})
                    case .XYZ : getXYZDataFromServer(completion: {})
                        }}
                    else {
                    switch (self, completion!()) {
                    case (let .X(arg1, arg2, arg3), let .X(comp)): getXFromServer(arg1: arg1, arg2: arg2, arg3: arg3, completion : comp)
                    case (let .Z(arg1), let .Z(comp) ) :    getZDataFromServer(arg1: arg1, completion: comp)
                    case (.Y, let .Y(comp)) :   getYDataFromServer(completion: comp)
                    case (.XYZ, let .XYZ(comp)) :   getXYZDataFromServer(completion: comp)
                    default: break
                        }
                        }
                  }
            }
            let firstY =  MixGetData.Y

            firstY.run()
            firstY.run{MixCompletion.Y{bool in print (bool)}}

Another way is to use generic function. Also you may combine both:

      func getYDataFromServer(completion: @escaping (Bool)->Void){
            //  session.dataTask(with: URLRequest()) { data, response, error in
            //    handleServerResponse(){ valid
            valid.toggle()
            print(valid)
            if  valid
            {
                getData(name: "XYZ", array: "") { getData(name: "Y", array: "", completion: completion)}
            }
            else {
                completion(false)
            }
            // }
            //  }.resume()
        }

        func getData<T>(name: String , array: Any... , completion:  T ){
            switch name {
            case "Y":
                getYDataFromServer(completion: completion as! (Bool)-> Void)
            case "X":
                let arg1 =  array[0] as! String; let arg2 = array[1] as! Int; let arg3 = array[2] as! Bool
                getXFromServer(arg1: arg1, arg2: arg2, arg3: arg3, completion: completion as? (String)-> Void)
            case "Z":
                let arg1 = array[0] as! Int
                getZDataFromServer(arg1: arg1, completion: completion as! (String)-> Bool)
            case "XYZ":
                getXYZDataFromServer(completion: completion as! ()-> Void)
            default:
                break;
            }
        }

        getData(name:  "Y",  array : "", completion:  { bool in print (123) } as (Bool)-> Void )

I know it's annoy if there are more than four types to input. But this is the only way to write a safe code. Let us know if you have the luck.

If you just need to complete the result, maybe this is what you want.

 func handleServerResponse<T, U>(_ alternative :  (T) -> () ,  _ completion : @escaping (U) -> (), _ handler : U , _ terminator : () , _ resValid : Bool){
if  resValid   {
    alternative( completion(handler) as! T )
}
else {
    terminator
}
}


  func getYDataFromServer(completion: @escaping (Bool)->Void){

        response.toggle()


       //  session.dataTask(with: URLRequest()) { data, response, error in


                  handleServerResponse({(a) in getXYZDataFromServer {a
                }},  { (a:  @escaping (Bool)->Void)  in getYDataFromServer(completion: a) }, completion,  completion(true), response)         

or

             handleServerResponse( { (a)   in getXYZDataFromServer{a}} , {  (a:  @escaping (Bool)->Void)   in getZDataFromServer(arg1: 1, completion: { (s) -> Bool in
                a
               return false
            })}, completion,  completion(true), response)

It's working well for me.

E.Coms
  • 11,065
  • 2
  • 23
  • 35
  • Hi, thanks for taking the time to respond. Unfortunately these approaches are not what I'm looking for if I still have to ```switch``` on different types. – Merricat Mar 20 '19 at 21:36