14 Replies
      Latest reply: Sep 8, 2016 9:30 AM by eskimo RSS
      fbartolom Level 1 Level 1 (0 points)

        I have this vanilla piece of code I virtually use everywhere in my code:

        let configuration = URLSessionConfiguration.default
        let session = URLSession(configuration:configuration)
        let listTask = session.dataTask(with: theRequest, completionHandler:{[weak self](data, response, error) in
        })
        
        
        
        

        Yet in one particular class the compiler reports:

        Cannot invoke 'dataTask' with an argument list of type '(with: URLRequest, completionHandler: (Data?, URLResponse?, Error?) -> ())' Expected an argument list of type '(with: URLRequest, completionHandler: (Data?, URLResponse?, Error?) -> Void)'

        How does it infer the return value of the closure to be () instead of Void?

        I copied the code from other classes repeatedly lest I wrote something wrongly.

        • Re: Cannot invoke 'dataTask' with an argument list of type...
          ahltorp Level 3 Level 3 (410 points)

          () is the same as Void, or at least should be handled the same way.

           

          func takesclosure(f: (foo: Any) -> Void) { f(foo: 1) }
          let a = { (foo) -> () in print(foo) }
          takesclosure(f:a)
          
          

           

          compiles even though a closure returning () is sent to a function expecting a closure returning Void.

           

          There is probably something else wrong here. Sometimes the error reporting in Swift is confused when it detects an errror and reports the wrong one.

           

          I would try to be more specific when writing the closure, and actually write

           

          (data: Data?, response: URLResponse?, error: Error?) -> Void
          

           

          Being more specific is often a way to make the error reporting better.

            • Re: Cannot invoke 'dataTask' with an argument list of type...
              fbartolom Level 1 Level 1 (0 points)

              Of course I know, given I use that same piece of code in many other places in my app. I also tried adding the esplicit ->Void annotation to no avail. I think it is a bug of the compiler that yet unfortunately does not allow my app to be compiled and run.

                • Re: Cannot invoke 'dataTask' with an argument list of type...
                  QuinceyMorris Level 7 Level 7 (3,570 points)

                  Try:

                   

                  (a) putting a 'return ()' in the closure, or

                   

                  (b) removing the [weak self]

                   

                  — assuming your closure really is empty of statements.

                    • Re: Cannot invoke 'dataTask' with an argument list of type...
                      fbartolom Level 1 Level 1 (0 points)

                      Removing the weak annotation is impossible as thta would create a memory leak. I might try instead to enter the return command at the end of the closure, whatever that means.

                        • Re: Cannot invoke 'dataTask' with an argument list of type...
                          QuinceyMorris Level 7 Level 7 (3,570 points)

                          Is the closure empty (devoid of non-declarative statements) as you originally posted?

                           

                          — If yes, then removing [self weak] will have no effect, since the closure doesn't actually capture 'self'.

                           

                          — If no, then you didn't give us a code fragment that actually produces an error, so how can anyone suggest a workaround?

                           

                          You also didn't say which version of Xcode you're using.

                            • Re: Cannot invoke 'dataTask' with an argument list of type...
                              fbartolom Level 1 Level 1 (0 points)

                              In fact by adding return (), in the closure, for some reason I ignore, it compiles fine. Of course I did include in my sample the piece of code capturing self.

                                • Re: Cannot invoke 'dataTask' with an argument list of type...
                                  QuinceyMorris Level 7 Level 7 (3,570 points)

                                  >> I did include in my sample the piece of code capturing self

                                   

                                  You wrote:

                                   

                                  let listTask = session.dataTask(with: theRequest, completionHandler:{[weak self](data, response, error) in
                                  })
                                  

                                   

                                  There is no code at all in this closure (that is, between "in" and the closing brace), so 'self' is not captured by the block. I also know this because, when I tried your code in a playground (Xcode 8 beta 6), I got a compiler warning that self wasn't captured by the block, so '[weak self]' was unnecessary.

                                   

                                  >> for some reason I ignore, it compiles fine

                                   

                                  Most likely because the explicit 'return' statement means that the compiler doesn't need to infer the type of value being returned by the closure, in order to compare it to the declared return type.

                                   

                                  Various versions of Swift have had trouble with this for a while now, which is why the Xcode version matters, and why adding redundant code sometimes helps.

                              • Re: Cannot invoke 'dataTask' with an argument list of type...
                                eskimo Apple Staff Apple Staff (6,785 points)

                                Removing the weak annotation is impossible as thta would create a memory leak.

                                This is a common misconception.  The weak self dance is only necessary if all the strong references in the cycle are persistent.  That’s not the case here.  While the URLSessionDataTask does hold a strong reference to the closure you pass it, that reference is released when the task completes.  As long as you actually start the task (by calling resume()), the task will eventually complete and that breaks the retain cycle.

                                Share and Enjoy

                                Quinn “The Eskimo!”
                                Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                let myEmail = "eskimo" + "1" + "@apple.com"

                                  • Re: Cannot invoke 'dataTask' with an argument list of type...
                                    fbartolom Level 1 Level 1 (0 points)

                                    As said the problm went away by adding return () a the end of the cloure, o the weka issue becomes superflous. At any rate this is the full snippet of code:

                                     

                                            let configuration = URLSessionConfiguration.default
                                            let session = URLSession(configuration:configuration)
                                            let listTask = session.dataTask(with: theRequest, completionHandler:{[weak self](data, response, error) in
                                                self?.processData(data, error:error)
                                                return ()
                                            })
                                            listTask.resume()
                                    

                                    As you see self is well captured as the function in most cases exits before the dataTask returns. I even had to use weak because the user might dismiss this controller even before the closure is called, so putting self to nil.

                                      • Re: Cannot invoke 'dataTask' with an argument list of type...
                                        ahltorp Level 3 Level 3 (410 points)

                                        Next time, please post the full snippet the first time. It is very difficult for others to infer what you "of course know" and what you have hidden.

                                          • Re: Cannot invoke 'dataTask' with an argument list of type...
                                            fbartolom Level 1 Level 1 (0 points)

                                            It is not always easy to select the right amount of code to post, from a single line of code, to a function, to the entire class: too much text tends to miss the issue, too less to create false answers. In my case I think I reasonably hit the spot given, of the two answers I was given, one was not applicable but the other fixed my problem for the good :-)

                                          • Re: Cannot invoke 'dataTask' with an argument list of type...
                                            eskimo Apple Staff Apple Staff (6,785 points)

                                            As said the problm went away by adding return () a the end of the cloure, o the weka issue becomes superflous.

                                            Right.  My point is that lots of folks advocate the weak self dance without really understanding what it does, and I’m trying to explain why it’s not necessary in many cases.

                                            As you see self is well captured as the function in most cases exits before the dataTask returns. I even had to use weak because the user might dismiss this controller even before the closure is called, so putting self to nil.

                                            Indeed.  And this approach makes sense in some limited circumstances.  However, in most cases it’s better to push your networking code down, out of your view controller layer and into a networking layer that’s tied to your model.  Then your view controller just shows model objects and isn’t concerned with the networking.

                                            Share and Enjoy

                                            Quinn “The Eskimo!”
                                            Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                            let myEmail = "eskimo" + "1" + "@apple.com"

                                              • Re: Cannot invoke 'dataTask' with an argument list of type...
                                                fbartolom Level 1 Level 1 (0 points)

                                                That would make very much Android programming... frankly I do not see so much the need to create so many layers in an object oriented environment but rather have in my full view the full piece of code I am working with. But of course this is a matter of taste. Somone like it horizontal, some othe ones like it vertical :-) It mins me of the bias, also being enforced in the WWDC, towards the have relationoship, oppsotie to the is one (property verus inheritance). OO theory has weel gaounded criteria to decide the relationships bwtween classes o to better model reality and I hold any attempt to steer somewhere for this make for not so easily maintanable code. But again this is a matter of taste.

                                                  • Re: Cannot invoke 'dataTask' with an argument list of type...
                                                    eskimo Apple Staff Apple Staff (6,785 points)

                                                    … I do not see so much the need to create so many layers in an object oriented environment …

                                                    OK, I’ll give you a concrete example of why this is important.  Imagine you have an iPhone app that shows a gallery of photos downloaded from the ’net.  You might think that it’s OK to do your networking in your gallery view controller.  However, consider what happens if the user:

                                                    1. navigates in to your gallery view

                                                    2. taps the back button to navigate out

                                                    3. navigates in again

                                                    This results in two different view controllers, one in step 1 and another in step 3.  The network requests issued by the view controller in step 1 get thrown away when the user switches out, and the view controller in step 3 has to start again from scratch.  This is clearly less than ideal.

                                                    Share and Enjoy

                                                    Quinn “The Eskimo!”
                                                    Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                    let myEmail = "eskimo" + "1" + "@apple.com"