Есть ли способ заставить сам вызов функции без повторного указания всех ее аргументов? - PullRequest
2 голосов
/ 20 марта 2019

Если кто-то может придумать другой способ добиться того, что я пытаюсь сделать, ваши мысли приветствуются!

Чтобы дать вам представление, у меня есть более 40 различных функций с сигнатурами, подобными этим:

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)

У некоторых есть аргументы, у некоторых нет; некоторые имеют обработчик завершения, а некоторые являются необязательными. Все виды типов передаются вокруг.

Я пытаюсь упростить процесс обработки ответа сервера: в настоящее время на нем много повторяющегося кода, все делают почти одно и то же, только с разными значениями аргументов и типами.

Поскольку все эти функции асинхронные, я использую обработчики завершения для обработки данных сервера при их получении. Я хочу, чтобы весь «повторяющийся код» входил в одну функцию, назовем ее handleServerResponse: в зависимости от значения ответа сервера, я хочу сделать больше асинхронных операций и иметь возможность повторно выполнить то же самое внешняя функция

Например:

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()
}

В настоящее время я выполняю повторное выполнение за пределами handleServerResponse, например:

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()
}

Это очень упрощенная версия кода, но, как вы можете видеть, он очень быстро повторяет множество повторяющихся кодов.

Спасибо

1 Ответ

0 голосов
/ 20 марта 2019

Может быть, это полезно

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

Но тип функции не может принудительно использовать Any, вы можете использовать enum для смешивания типов безопасности.

            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)}}

Другой способ заключается виспользовать универсальную функцию.Также вы можете комбинировать оба:

      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 )

Я знаю, это раздражает, если есть более четырех типов для ввода.Но это единственный способ написать безопасный код.Дайте нам знать, если вам повезет.

Если вам просто нужно завершить результат, возможно, это то, что вы хотите.

 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)         

или

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

У меня это хорошо работает.

...