BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

Stuck on Day 9 - closures - beginner guidance sought!

Forums > 100 Days of SwiftUI

Hello folks, and thank you so much Paul for this resource. I feel like I'm learning a ton 😁

But... first day learning about closures has hit my brain hard!

For the challenge/checkpoint, I've made a start, got the right structure (from the earlier examples), and have started to make some progress but immediately got stuck.

I've changed the first closure to return an array of Int. Yay. But for the life of me I can't get it to accept an array as input. Am I not meant to do that?

It's been a while since I've used git but hopefully this link will let you see my code: https://github.com/davenewt/100Days-SwiftUI-Day9

Thanks in advance for any guidance! πŸ™

   

Where are your 'luckyNumbers' created - I see them being used, but I don't see them defined. (i.e. let luckyNumbers = [1,2,3,4,5]). BTW, Swift starts arrays at element 0.

1      

Welcome to Hacking With Swift!

Your question might be very useful to a new programmer one, maybe three years from now. Please post your smaller code snips directly in your forum message!

Reading a Signature

It's early days in your Swift journey. So please make lots of errors, revise your code and continue learning. One important lesson you need to practice over and over is How to Read Function Signatures.

For example, this is your function's signature:

func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) { 
    // do magic here
}

Can you read this signature? Does this make sense to you? You need a skill to read this signature OUT LOUD like it's a sentence. Keep reading it until it makes sense to you.

Here's what your signature says:

//             1️⃣                 2️⃣                   3️⃣
func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) {

Your function tells a programmer that it needs three inputs, named first, second, and third.

//-------
//             1️⃣  πŸ‘‡πŸΌ No parameters requiried
func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) {
//                          πŸ‘†πŸ½ But the function MUST return an array of integers
// 1️⃣ The first input is a function that takes NOTHING, but returns an array of Integers
// Please note! Your first function is NOT accepting an array of Integers.
// You're telling the compiler the first function accepts NOTHING

//-------
//                                 2️⃣   
func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) {
// 2️⃣ The second input is a function that takes NOTHING and returns NOTHING

//-------
//                                                    3️⃣ 
func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) {
// 3️⃣ The third input is another function that takes NOTHING and returns NOTHING

Yikes! I don't think this is what you want to do!

Additional Reading

Here's a short response to another new programmer who was learning to read function signatures.

See -> Signatures! Read All About It

Keep Coding!

More feedback! First, Second, and Third are awful parameter names!

1      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, 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!

Forum regular @roosterBoy posted a great article on how to post code bits to this forum.

See -> Posting Code to the Forum

Go on! Give it a try!

Keep Coding!

1      

@cacruden – thanks for the reply... the array is being defined at the top of the code.

@Obelix – thanks also! Ok, here's my code pasted into the forum...

let luckyNumbers = [7,4,38,21,16,15,12,33,31,49]

func doStuff(first: () -> [Int], second: () -> Void, third: () -> Void) {
    print("Filtering numbers...")
    first()
    print("Sorting...")
    second()
    print("Creating strings...")
    third()
    print("Done. Result:")
}
doStuff {
    var filteredNumbers = [Int]()
    for x in 1..<luckyNumbers.count {
        if !luckyNumbers[x].isMultiple(of: 2) {
            // add it to our output array
            filteredNumbers.append(luckyNumbers[x])
        }
    }
    print(filteredNumbers) // OK so at least this bit's working
    return filteredNumbers
} second: {

} third: {

}

I understand what the func doStuff(first: () -> [Int]... line is doing, but didn't know this was called "reading a signature" – that's a new phrase to me, so thank you!

I've only started with the first closure and haven't touched the 2nd and 3rd, so I know that's not what I actually need to be doing.

I thought I first needed to get first to accept and [Int] but I also now see, when I run this, that the for x in 1..<luckyNumbers.count loop does do things to the luckyNumbers array, so I maybe don't need to explicitly pass in an array?

Am I on the right lines so far?

Hopefully also this code shows up OK, I don't see a preview button on the forum here! 🀞

   

Hopefully also this code shows up OK, I don't see a preview button on the forum here! 🀞

Whilst typing your forum message, you'll see a small eyeball πŸ‘οΈ above the text box where you type. Click once for a preview!

Click again to exit preview mode and return to editing mode.

ALSO, you can always return to a post that YOU WROTE and edit it over and over again.

Keep Coding

   

@daveAsaur:

You might be going down the wrong path. Here's a code snip for you to review. But inside this code snip is a BIG HINT on how you should proceed. Please keep making mistakes. It's a great way to challenge yourself and learn.

// Paste into Playgrounds and RUN

// This is a function that takes NOTHING as input and returns an array of Integers
//  Hover your mouse over dataProvider_1 and hold down the option key.
//   πŸ‘‡πŸΌ When the cursor changes to a ? click ONCE to see its definition.
let dataProvider_1 = {
    return [3, 4, 5, 7]
}
// Convince yourself that this is a *function* that takes no input and 
// returns an array of Int.

// This is a function that takes NOTHING as input and returns an array of Integers
let dataProvider_2 = {
    return [32, 42, 52, 82, 92, 99, 1102, 202, 22022]
}

// This is a function that takes NOTHING as input and returns an array of String
let dataProvider_3 = {
    return ["dave", "obelix", "twoStraws", "taylor"]
}

// Read the signature
// This is a FUNCTION that takes another function as its only parameter and
// returns an array of Integers.
// The input parameter is named inputFunction
// inputFunction must be a function that takes NOTHING and returns an array of Int
func filterDataForOddNumbers( inputFunction: ()->[Int] ) -> [Int] {
    var onlyOddNumbers = inputFunction()

    // Hint!!
    //                      πŸ‘‡πŸΌ This is a FUNCTION written as a closure!
    return onlyOddNumbers.filter{ !$0.isMultiple(of: 2)}
    //         πŸ‘†πŸ½ This is an array of integers!
}

print("Data Provider 1")
//    Provide a function as a parameter   πŸ‘‡πŸΌ
filterDataForOddNumbers(inputFunction: dataProvider_1)

print("Data Provider 2")
//    Provide a function as a parameter   πŸ‘‡πŸΌ
filterDataForOddNumbers(inputFunction: dataProvider_2)

print("Data Provider 3")
//    Provide a function as a parameter   πŸ‘‡πŸΌ
filterDataForOddNumbers(inputFunction: dataProvider_3)  // What happens here?

Note! In the code above, filterDataForOddNumbers isn't necessary! The hint should push you in the right direction.

Keep Coding

   

@Obelix thank you so much for your patience. I'm trying hard to wrap my brain around this, I really am.

// function (closure?) that returns an [Int]
let luckyNumbers = {
    return [7,4,38,21,16,15,12,33,31,49]
}

// function that takes another function (the above closure) as its input
func filterDataForOddNumbers( inputFunction: ()->[Int] ) -> [Int] {
    var onlyOddNumbers = inputFunction()
    return onlyOddNumbers.filter{ !$0.isMultiple(of: 2)}
}
// call the function and pass the input array (closure) as its input function (I think I understand this bit)
filterDataForOddNumbers(inputFunction: luckyNumbers)

// Alright so... let's define another function that takes another function as its only parameter and returns [Int]
func sortNumbers( inputFunction: ()->[Int] ) -> [Int] {
    var sortedNumbers = inputFunction()
    return sortedNumbers.sorted()
}

// I can then do this (on the original array)... 
sortNumbers( inputFunction: luckyNumbers)
// BUT I CANNOT DO THIS?...
sortNumbers( inputFunction: filterDataForOddNumbers)
// OK so filterDataForOddNumbers is a function() rather than a closure so I need '()'?
sortNumbers( inputFunction: filterDataForOddNumbers())
// nope, more errors!
sortNumbers( inputFunction: filterDataForOddNumbers(inputFunction: luckyNumbers))
// OMG even worse!

I've seen people commenting on the original video saying "I was able to do this with one line of code!" so why am I writing so many lines of code and seemingly nesting functions (which can't be a good idea) and still not getting it?! I feel SOOOO stupid 😒

   

OK I've gone back to Paul's videos again !

This part helped https://youtu.be/HnIID4W3a_o?t=420

And this code works...

let allNumbers = [7,4,38,21,16,15,12,33,31,49]
// create a new array, leaving an item ($0) if it is NOT (!) a multiple of 2
// we are calling the 'filter' function using trailing closure syntax
let oddOnly = allNumbers.filter { !$0.isMultiple(of: 2) }
// create another new array with numbers sorted (default sort)
let sortedOdd = oddOnly.sorted()
// create another array that puts the items into separate strings
let stringifiedSorted = sortedOdd.map { "\($0) is a lucky number" }
// print out the strings, one per line
for number in stringifiedSorted {
    print(number)
}

I will admit that I have the predictive code completion enabled (running xcode beta) so I should probably turn that off, because it auto-completed the .map part for me.

(and in Paul's video he covers 'map' which I'd forgotten existed, here: https://youtu.be/HnIID4W3a_o?t=484)

@Obelix – is there a reason you were suggesting

let luckyNumbers = {
    return [7,4,38,21,16,15,12,33,31,49]
}

rather than simply let allNumbers = [7,4,38,21,16,15,12,33,31,49]

?

I feel like I might be missing a big chunk of what you were trying to teach me! Apologies if this is the case!

   

And now I understand how THIS works...

let allNumbers = [7,4,38,21,16,15,12,33,31,49]
// You can write multiple steps on one line: filter + sort + map...
let answers = allNumbers.filter{!$0.isMultiple(of: 2)}.sorted().map{"\($0) is a lucky number"}
for line in answers {
    print(line)
}

   

@Obelix – is there a reason you were suggesting

let luckyNumbers = {
    return [7,4,38,21,16,15,12,33,31,49]
}

rather than simply let allNumbers = [7,4,38,21,16,15,12,33,31,49]

Great Question!

The answer is Yes!

// 1️⃣ Example 1
// This is correct:
//    πŸ‘‡πŸΌ this is a constant defined as an array of Integers
let  allNumbers = [7,4,38,21,16,15,12,33,31,49]

// 2️⃣ Example 2
// This is also correct, but hardly ever used. It's just an illustration to you
//    πŸ‘‡πŸΌ this is a function! Yes it's a function!
let luckyNumbers = {
    return [7,4,38,21,16,15,12,33,31,49]
}
// It's a function that takes nothing as a parameter, but returns an array of Integers

How can you tell the second example is a function?

This is a skill you need to learn!

Hover your mouse over luckyNumber then press the option key. When the cursor changes to a ? click ONCE to see its definition.

Xcode will show you that one is an array. The other is a function.

LEARN THIS TRICK.

Keep Coding

1      

This is a skill you need to learn!

OK I understand that example 2 is a function which returns an [Int] – essentially a simpler way of writing:

func luckyNumberz() -> [Int] {
    return [7,4,38,21,16,15,12,33,31,49]
}
let myLuckyNumberz = luckyNumberz()

?

But you also say it's "hardly ever used" so... why is it important to know? (or should I stop stressing about it and it'll become clear later in the course?) πŸ™‚

Thank you so much for your help.

   

But you also say it's "hardly ever used" so... why is it important to know?

I should have clarified. Using a function to define an array of numbers is NOT the best use case. In this lesson, the point you want to learn is that just as you can assign an Int to a variable. Or you can assign an array to a variable. Or you can assign a string to a variable. You can ALSO assign a function to a variable. This is the take-away lesson.

You will write functions and assign them to variables. And you'll write flexible functions and use them in closures. You will hardly ever write a function to return an array of static integers.

Keep Coding

I added two more lessons.

  1. It's important that you know how to read a function's signature.
  2. It's important that you know how to option-click a variable to see the type that Xcode has determined for this variable.

1      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, 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!

Reply to this topic…

You need to create an account or log in to reply.

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.