WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

Day 9 - How to Accept Functions as Parameters - example confusion

Forums > 100 Days of SwiftUI

I'm having a minor comprehension crisis with the following example from https://www.hackingwithswift.com/quick-start/beginners/how-to-accept-functions-as-parameters

func makeArray(size: Int, using generator: () -> Int) -> [Int] {
    var numbers = [Int]()

    for _ in 0..<size {
        let newNumber = generator()
        numbers.append(newNumber)
    }

    return numbers
}

let rolls = makeArray(size: 50) {
    Int.random(in: 1...20)
}

print(rolls)

In the example, we're only passing a single parameter into makeArray, but when I try to replicate this in my Playground I get an error, since makeArray requires two parameters, however I have no idea what to pass in as the second parameter.

e.g. let rolls = makeArray(size: 50, ?something?)

Have I missed the point? Do I need to define generator somewhere else? What does generator actually do?

2      

You don't indicate exactly what error you are getting in the playground, so it's a little difficult to assess what's wrong with your code, but in the example you give you are actually passing two parameters to makeArray, not one.

The signature of makeArray is:

func makeArray(size: Int, using generator: () -> Int) -> [Int]

meaning that the second parameter is a function that takes no input and returns an Int. So given a function called, for example, generateRolls, you would call it like so:

func generateRolls() -> Int {
    Int.random(in: 1...20)
}

let rolls = makeArray(size: 50, using: generateRolls)

But we can simplify by replacing the explicit function generateRolls with a closure:

let rolls = makeArray(size: 50, using: { Int.random(in: 1...20) })

Swift has a nifty feature called trailing closures, where if the final parameter is a closure* you can write the closure after the closing parentheses "even though the trailing closure is still an argument to the function", that allow us to clean up that call site:

let rolls = makeArray(size: 50) {
    Int.random(in: 1...20)
}

So you are still passing two parameters to makeArray, even if it doesn't look like it.

Admittedly, it takes some getting used to when you first pick up Swift, but it really makes your code easier to read and nicer looking.


*And, since last year, you can even have multiple trailing closures. So a something like this, that takes two function params:

Button.init(action: () -> Void, label: () -> Label)

can be written as:

Button {
    //do some action here
} label: {
    //show some label here
}

You'll notice there are no parentheses but you are still calling the init function with two parameters.

4      

Thanks for the reply, @roosterboy.

I'll try to explain my issue, but I'm already feeling out of my depth since the beginning of Day 9 of 100, so please bear with me.

The examples I posted are copied verbatim from the website, so I'm just trying to understand how they (don't) work.

I'm getting the error "Missing argument for parameter 'using in call'" on the line:

let rolls = makeArray(size: 50)

So, I did attempt to put the additional parameter into it, like:

let rolls = makeArray(size: 50, using generator)

Playground then gives me the error "Cannot find 'using' in scope"

I'm more of a "follow the examples" kind of learner, so I get thrown off easily when I copy something verbatim and can't get it to work.

It's likely that I don't understand the concepts well enough, but after repeating Day 9 three times I'm wondering whether I'm just not up to the task.

2      

let rolls = makeArray(size: 50, using generator)

Since using is an argument label, you need to follow it with a colon :

let rolls = makeArray(size: 50, using: generator)

2      

@snaketide thank you for this question, my head was hurting too....

@roosterboy thank you for your explanation seeing the following together on screen helped me see what was happening

 let rolls = makeArray(size: 50, using: { Int.random(in: 1...20) })

Swift has a nifty feature called trailing closures, where if the final parameter is a closure* you can write the closure after the closing parentheses "even though the trailing closure is still an argument to the function", that allow us to clean up that call site:

let rolls = makeArray(size: 50) {
    Int.random(in: 1...20)
}

at the moment I find this easier to read before it's converted to a trailing closure, the closed parentheses threw me.... I'm getting there though... but I'm not looking forward to checkpoint 5, I think it's going to hurt, lol

2      

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.