2

I have a method that iterate a list of category class, and return items from all the categories in the end. the simple code look like this:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void)
{
    for category in allCategories
    {
        item(category)
    }
}

when using it:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

so far so good, but now i want to add an optional completion to this method, so i changed the code to:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ completion:(() -> Void)? = nil)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

But now when i'm trying to use the method like this:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

The compiler shows an error:

Missing argument for parameter 'item' in call.

So, what i am doing wrong?

Asi Givati
  • 1,348
  • 1
  • 15
  • 31

3 Answers3

2

When you use the trailing {} at the end of the function the compiler will automatically assign it to the last parameter of your function (to the completion closure) and so it thinks the items parameter is missing from your call

You could use it like that:

iterateMyCategoriesItems (items:{ (category) in

   // doing something here with every category...
})

or if you want to have a completion

iterateMyCategoriesItems (items: { (category) in

   // doing something here with every category...
}) { _ in 
  //do things when completed
}
Or Ron
  • 2,313
  • 20
  • 34
1

You should be able to identify that you are using trailing closures in this call:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

Swift compiler detects that this is a trailing closure and so it matches it to the last argument of the method, which is the completion handler.

Now that it has done that, it continues to look for other arguments that match the first argument. None is found so it complains.

To fix this, you have to give up on using a trailing closure and pass the argument properly:

iterateMyCategoriesItems(items: { (category) in

   // doing something here with every category...
})

You can also remove the argument label to make this cleaner.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
1

The Swift book describes the Trailing Closure as follows:

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead.

(Bold style added.)

It clearly address the target argument as final.

Seems this rule applies even if the final argument is optional.

//-> warning: closure parameter prior to parameters with default arguments will not be treated as a trailing closure
func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ someOptionalIntParam: Int? = nil)
{
    for category in allCategories
    {
        item(category)
    }
}

//-> error: missing argument for parameter 'item' in call
iterateMyCategoriesItems { (category) in

    // doing something here with every category...
}

As already noted in other answers, if you want to keep your function definition as is:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ completion:(() -> Void)? = nil)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

(This is another topic, but you should avoid (Void)->Void, if you want to write a closure type with no arguments. See this thread.)

you need to call it like:

iterateMyCategoriesItems(item: { (category) in

    // doing something here with every category...
})

If you want to write item closure always as a trailing closure, you can modify your function definition like this:

func iterateMyCategoriesItems(completion:(() -> Void)? = nil, item:(_ category:Category) -> Void)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

//Does not cause error
iterateMyCategoriesItems { (category) in

    // doing something here with every category...
}

But, you may not prefer putting completion closure before other closures.

OOPer
  • 47,149
  • 6
  • 107
  • 142