var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var stdin_exports = {}; __export(stdin_exports, { default: () => _2021_02_17_closures_in_swift, metadata: () => metadata }); module.exports = __toCommonJS(stdin_exports); var import_index_10ac95e2 = require("./index-10ac95e2.js"); const metadata = { "title": "Swift Closures: Inline functions explained by a web developer", "author": "Thomas Wilson", "date": "2021-02-17T00:00:00.000Z", "draft": false, "slug": "2021-02-17-closures-in-swift", "tags": ["swift"] }; const _2021_02_17_closures_in_swift = (0, import_index_10ac95e2.c)(($$result, $$props, $$bindings, slots) => { return `
Hi, I\u2019m Thomas. I\u2019m a frontend engineer who\u2019s learning swift. Let\u2019s talk about closures in Swift from a very (very) introductory level. I\u2019m assuming you\u2019ve got some familiarity with JavaScript. You should definitely check out Apple\u2019s documentation on Closures, it\u2019s so much better than this page but also, like, less funny?
Closures are inline function definitions.
We have inline functions all over the place in JavaScript. Callback functions, including those in promises, are often declared in JavaScript:
${`// Ever written an express routers?
router.use('/users', (req, res, next) => { .. })
// Ever write a promise?
fetch("https://www.google.com").then((res) => { .. })`}
Like JavaScript, Swift has functions as a first-class citizen. That means they can be passed around like any other variable. That\u2019s pretty cool, and if you come from a pure JS background you might not realise it. You might think it\u2019s pretty annoying. But it\u2019s not. Try filtering a dataframe into a subset in Python using Pandas without the filter-like function syntax (don\u2019t @ me).
We use closures in Swift in exactly the same use case: when we need to pass a function as an argument to a function. For example, if we are passing a custom sort, map, or sorted function on an array.
In the same mental model that React uses, a SwiftUI View is like a React component: it\u2019s fundamentally a function: f(state) => ui - UI is a function of state.
Understanding Closures in swift will help you write, and read, SwiftUI examples.
The name\u2019s a little confusing. In JavaScript a closure is the scope at which a function is declared and its relationship to the surrounding variables. Coming from JS, I had a little trouble getting my head around them.
Defining inline, full-blooded functions in Swift is pretty standard, and could probably be intuited by any engineer with one or two languages under their hat:
${`func multiply(number: Int, by:Int) -> Int {
return number * b
}`}
It would be perfectly valid syntax to pass this function around by simply referencing its identifier (multiply).
But what if we don\u2019t want to have to declare named functions everywhere. Especially if we\u2019re literally just going to use it once?
In JavaScript you define the function inline with the same syntax as you would anywhere else. BUT NOT IN SWIFT. Why? I don\u2019t know, friend, by let\u2019s explore the what not the why first.
Swift comes with a set of syntactical sugar for declaring closures. Syntactical sugar is a way of making code shorter or more readable. Syntactical sugar often replaces boilerplate or verbose code, and results in identical functionality to its un-sugared sibling.
This syntactical sugar looks like:
${` { (parameters) -> ReturnType in
// expression
}`}
Let\u2019s give a real simple example. We\u2019re going to take an array of Int and convert it to an Array<String>:
${`let strings: Array<String> = [1,2,3,4,5].map({ (n: Int) -> String in
return "The number is (n)"
})`}
in keywordThis syntax looked odd to me. The key (pun intended) to me grokking it was understanding the in keyword. Typically I have only seen this associated with iterators (in languages like python and JS):
${`# This is Python code
for name in list_of_name:
print(f"Hello, {name}")`}
In Closures, in Swift, the in keyword signifies that we\u2019ve reached the end of our parameter and return type. Everything after the in is the action function expression.
You better believe that\u2019s not all the syntactic sugar. There\u2019s still stuff we\u2019re going to get rid of.
Once we declare types in one place, e.g. in a variable, generic, parameter, then we don\u2019t need to duplicate that typing. We can but we don\u2019t have to.
Swift is able to find the implied types elsewhere in the code, and therefore we can remove them in the closure.
In the following example the boastfulString variable is declared as the Array<String> type, so the map function doesn\u2019t need to be told twice:
${`let boastfulStrings: Array<String> = [10,20,30,40,50].map( { n in return "I have (n) tacos!"})`}
By using implicit types we can get rid of two redundant parts of the closure declaration.
The surrounding parentheses for the parameters: (n: Int) becomes n). We can do the same with multiple parameters: Say we had an argument that took three parameters: (name: String, age: Int, averageScore: Float) .. could become name, age, averageScore ..
The return type of the closure: We know the function needs to return us a String, it\u2019s in the variable declaration. So (n: Int) -> Int in .. can become n -> ..
Swift is super ready for you to give it an Array<String> and will actually get pretty mad if you dont\u2019. You\u2019ll run into compile-time errors with anything else.
Man, talking of redundancies, that return doesn\u2019t look like it\u2019s doing much on that one line there. JS, Ruby, and Rust all have implicit return types - and so does Swift. That means you don\u2019t need to use the return keyword to tell Swift \u201Cthis is the bed that the function should hand back\u201D.
This is more syntactic sugar: we\u2019re choosing conciseness and simplicity over explicit and verbose. Having return or omitting it in this example does exactly the same thing. You don\u2019t have to like this, or us it in your code. It\u2019s your choice, but you should definitely know about it. Also it\u2019s probably useful in those scenarios where you just need something to work:
${`let boastfulStrings: Array<String> = [100, 150, 200].map( { n in "I have (n) taco!" })`}
Look how concise that statement is.
But we can be more concise.
What else can we get rid of?
That named parameter, n:
${`let regretfulStrings: Array<String> = [2,4,6,8].map( { "I ate ($0) too many tacos ):" })`}
These are shorthand arguments. Where $0 refers to the first argument in the function. $1 to the second argument, $2 to the third\u2026
Now we\u2019re really favouring conciseness over explicitness.
`; });