Swift closure omission example? - swift

When I read the Swift documentation provided by Apple, I found some concept not clear for me...
When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
What does it means the callback for a delegate? Could you give me an example? If both are omitted, should we need the in keyword?

There is nothing simpler. In this case the meaning of delegate is that the closure is used as a variable. Consider the following example:
class Downloader {
var onDownloaded: ((Data) -> Void)?
func startDownloading() {
...
}
}
Used as:
let downloader = Downloader()
downloader.onDownloaded = { data in
print("Downloaded: \(data.count) B")
}
downloader.startDownloading()
As you can see, I did not specify neither the type or the return value in the closure, because the left side of the expression (.onDownloaded =) provides them.
The same applies for method parameters:
func startDownloading(onDownloaded: ((Data) -> Void)?) {
...
}
However, we still need in in the closure. The keyword separates the parameter names from the closure body. Of course, we could make the parameters anonymous:
downloader.onDownloaded = {
print("Downloaded: \($0.count) B")
}

It states that the parameter type can be inferred from the delegate. A delegate is a protocol, which is where you define the types of the method parameters. This means that when implementing the delegate method, the compiler already knows about the method types through the declared protocol.
An example:
let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in
return one < two
}
The first simplification is related to the parameters. The type inference system can calculate the type of the parameters in the closure:
let sortedAnimals = animals.sort { (one, two) -> Bool in return one < two }
The return type can also be inferred:
let sortedAnimals = animals.sort { (one, two) in return one < two }
The $i notation can substitute the parameter names:
let sortedAnimals = animals.sort { return $0 < $1 }
In single statement closures, the return keyword can be omitted:
let sortedAnimals = animals.sort { $0 < $1 }
For strings, there's a comparison function which makes string comparison even shorter:
let sortedAnimals = animals.sort(<)
Each step outputs the same result and it is for you to decide what is concise, but readable at the same time.

Related

Is there an Kotlin equivalent `with` function in Swift?

In Kotlin, we could change the below
// Original code
var commonObj = ClassCommonObj()
commonObj.data1 = dataA
commonObj.data2 = dataB
commonObj.data3 = dataC
// Improved code
var commonObj = ClassCommonObj()
with(commonObj) {
data1 = dataA
data2 = dataB
data3 = dataC
}
However in Swift as below, do I have equivalent with function to use?
// Original code
var commonObj = ClassCommonObj()
commonObj.data1 = dataA
commonObj.data2 = dataB
commonObj.data3 = dataC
Unfortunately, no such functionality so far in Swift. However, similar functionality can be reached with the power of extensions:
protocol ScopeFunc {}
extension ScopeFunc {
#inline(__always) func apply(block: (Self) -> ()) -> Self {
block(self)
return self
}
#inline(__always) func with<R>(block: (Self) -> R) -> R {
return block(self)
}
}
This protocol and extension provides two inline functions, where one can be served to return processed object, and the other is strictly similar to with in Kotlin and other languages (Visual Basic supported in 90s).
Usage
Specify types which these functions should apply to:
extension NSObject: ScopeFunc {}
apply:
let imageView = UIImageView().apply {
$0.contentMode = .scaleAspectFit
$0.isOpaque = true
}
Here we create an object and once the closure is executed, modified object is returned.
with:
imageView.with {
$0.isHidden = true
}
Works equal to with in Kotlin.
Originaly based on this source code.
NOTE:
Swift compiler is generally regarded as smart enough to decide whether or not a function should be inlined. Quite likely, these two would be inlined due to their relative compactness even without strictly specifying #inline (__always). Either way, you should know that this keyword does not affect the logic and the result of these, because inlining is about optimizing the program.
Like #Hexfire said, so far, no built-in Swift equivalent to Kotlin's with(). As he points out, you can more or less write one yourself.
I use a version slightly different than the Kotlin with() that automatically returns the modified element (more akin to Kotlin's apply). I find this clearer, pithier, and more generally useful.
This is the version I use:
#discardableResult
public func with<T>(_ item: T, _ closure: (inout T) -> Void) -> T {
var mutableItem = item
closure(&mutableItem)
return mutableItem
}
It's declared globally (so no dependency on NSObject or extension declarations). It also handles mutability like I expect. In use, it looks like:
let myWellDescribedLabel = with(UILabel()) {
$0.attributedText = attributedStringTitle
$0.isAccessibilityElement = true
$0.numberOfLines = 1
}
Unfortunately (or is it? see comments), Swift does not have self syntax in closures, so you must reference the passed object as $0 (or create a named parameter to the closure).
While we're here, a withLet() that handles optionals is also very useful:
#discardableResult
public func withLet<T>(_ item: Optional<T>, _ closure: (inout T) -> Void) -> Optional<T> {
guard let item = item else { return nil }
return with(item, closure)
}
These are in a gist here.

What does unnamed parameters must be written with the empty name '_' mean?

I'm learning Swift from a book and we are using Playgrounds to build out a class. I received an error that reads: unnamed parameters must be written with the empty name '_'.
I understand an underscore in Swift means "to ignore" but if I add an underscore followed by a space then I receive the error: Parameter requires an explicit type which is fairly easy to understand, meaning that a parameter must be declared as a certain type. :)
I'd like to know exactly what the error "unnamed parameters must be written with the empty name '_'" is trying to say in layman terms because its not making much sense to a noob like me.
Here is the code from the playground up to this point:
//: Playground - noun: a place where people can play
import UIKit
var str = "Hello, playground"
func fahrenheitToCelsius(fahrenheitValue: Double)-> Double {
var result: Double
result = (((fahrenheitValue - 32) * 5) / 9)
return result
}
var x = fahrenheitToCelsius(fahrenheitValue: 15.3)
print(x)
class Door{
var opened: Bool = false
var locked: Bool = false
let width: Int = 32
let height: Int = 72
let weight: Int = 10
let color: String = "Red"
//behaviors
func open(_ Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
func close(_ Void)->String{
opened = false
return "C-r-r-e-e-a-k-k-k...the door is closed!"
}
func lock(_ Void)->String{
locked = true
return "C-l-i-c-c-c-k-k...the door is locked!"
}
func unlock(_ Void)->String{
locked = false
return "C-l-i-c-c-c-k-k...the door is unlocked!"
}
}
I guess, your code was something like this, when you get unnamed parameters must be written with the empty name '_'.
func open(Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
Seems you are an experienced C-programmer.
In Swift, single-parameter functions (including methods) should have this sort of header:
func functionName(paramLabel paramName: ParamType) -> ResultType
When the paramLabel and paramName are the same, it can be like this:
func functionName(paramName: ParamType) -> ResultType
You can use _ both for paramLabel and paramName, so this is a valid function header in Swift, when a single argument should be passed to the function and it is not used inside the function body:
func functionName(_: ParamType) -> ResultType
But in old Swift, you could write something like this in the same case:
func functionName(ParamType) -> ResultType
Which is not a valid function header in the current Swift. So, when Swift compiler find this sort of function header, it generates a diagnostic message like: unnamed parameters must be written with the empty name '_' which is suggesting you need _: before the ParamType.
The actual fix you need is included in the Lawliet's answer. You have no need to put Void inside the parameter when your function takes no parameters.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
From my understanding, this is a practice from objective C that is carried and respected in swift. In objective C style, you name your parameters, but when you don't need them for description or readability purposes, you can just use _. Here's an example
init(_ parameter: Type)
Objective C protocols also follow this naming convention -
tableView(_ tableView: UITableView.......)
// in swift
protocol MyCustomProtocol: AnyObject {
func controller(_ controller: MyCustomControllerClass, DidFinishLoadingSomething something: Type)
}
When you do want to name your parameters in your functions, you can -
class CustomClass {
init(withUserId id: String)
}
// to use the above:
CustomClass(withUserId: "123123")
func insert(newIndexPath indexPath: IndexPath)
...
insert(newIndexPath: myNewIndexPath) // This is how you would use the above function
To help with your problem specifically, you specified that your func open does not need a parameter name. But you never specified what your parameter is. If you do want to pass a parameter, call it func open(_ open: Bool) -> String { , or if you don't want a parameter for that function, just use func open() -> String {
Parameter requires an explicit type. Therefore, the func open(_ Void)->String function declaration causes a compile error. If you just want to write a function that has no argument, remove _ Void.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
According to Apple's Swift book, the underscore (_) can be used in various cases in Swift.
Function: If you don't want an argument label for a parameter, _ can be used rather than having an explicit argument.
func sumOf(_ arg1: Int, arg2: Int) -> Int{
return arg1 + arg2
}
sumOf(1, arg2: 5)
Numeric Literals: Both Int and Float can contain _ to get better readability.
let oneBillion = 1_000_000_000
let justOverOneThousand = 1_000.000_1
Control Flow: If you don't need each value from a sequence, you can ignore the values by using an _, aka the Wildcard Pattern, in place of a variable name.
let base = 2
let power = 10
var result = 1
for _ in 1...power {
result *= base
}
Tuples: You can use _ to ignore parts of a tuple.
let http404Error = (404, "Not Found")
// Decompose to get both values
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// Decompose to get the status code only
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")

In Swift,there's no way to get the returned function's argument names?

When a function's return value is another function,there's no way to get the returned function's argument names.Is this a pitfall of swift language?
For example:
func makeTownGrand(budget:Int,condition: (Int)->Bool) -> ((Int,Int)->Int)?
{
guard condition(budget) else {
return nil;
}
func buildRoads(lightsToAdd: Int, toLights: Int) -> Int
{
return toLights+lightsToAdd
}
return buildRoads
}
func evaluateBudget(budget:Int) -> Bool
{
return budget > 10000
}
var stopLights = 0
if let townPlan = makeTownGrand(budget: 30000, condition: evaluateBudget)
{
stopLights = townPlan(3, 8)
}
Be mindful of townPlan,townPlan(lightsToAdd: 3, toLights: 8) would be much more sensible to townPlan(3, 8), right?
You're correct. From the Swift 3 release notes:
Argument labels have been removed from Swift function types... Unapplied references to functions or initializers no longer carry argument labels.
Thus, the type of townPlan, i.e. the type returned from calling makeTownGrand, is (Int,Int) -> Int — and carries no external argument label information.
For a full discussion of the rationale, see https://github.com/apple/swift-evolution/blob/545e7bea606f87a7ff4decf656954b0219e037d3/proposals/0111-remove-arg-label-type-significance.md

Swift, generic function: Why is one argument label needed, the other is not?

The Swift Playground has this function:
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
let strArray: [String] = repeatItem("knock", numberOfTimes:4) //!!!!
Why is there a numberOfTimes: in the function call and why does removing it give me the error "missing argument label"? More confusingly, why does adding an argument label to "knock" give me "extraneous argument label"?
EDIT:
Also this piece of code has not arguments labels in the call:
func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
Question 1
This is by construction of Swift. From Swift language guide for functions - Function Parameter Names:
By default, the first parameter omits its external name, and the
second and subsequent parameters use their local name as their
external name. All parameters must have unique local names. Although
it’s possible for multiple parameters to have the same external name,
unique external names help make your code more readable.
...
If you do not want to use an external name for the second or
subsequent parameters of a function, write an underscore (_) instead
of an explicit external name for that parameter.
Note from above that you can supersede this demand by placing an underscore _ in front of 2nd (and onward) parameter name. In your case:
func repeatItem<Item>(item: Item, _ numberOfTimes: Int) -> [Item] { ...
Finally note that this has nothing to do with generics, but with Swift functions in general.
Question 2
Try replacing your line
let strArray: [String] = repeatItem("knock", numberOfTimes:4) //!!!!
with
let strArray = [String](count: 4, repeatedValue: "knock")
This uses the initialiser for array objects with repeated entries.
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item]
is a method that takes two parameters, the first is item and the second is named numberOfTimes.
In Swift, when you call a method or a function you have to write the name of the parameters followed by ":" and the its value.
In Swift the name of the first parameter can be omitted.

Using a Type Variable in a Generic

I have this question except for Swift. How do I use a Type variable in a generic?
I tried this:
func intType() -> Int.Type {
return Int.self
}
func test() {
var t = self.intType()
var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}
This didn't work either:
var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.
Is there a way to do this? I get the feeling that Swift just doesn't support it and is giving me somewhat ambiguous error messages.
Edit: Here's a more complex example where the problem can't be circumvented using a generic function header. Of course it doesn't make sense, but I have a sensible use for this kind of functionality somewhere in my code and would rather post a clean example instead of my actual code:
func someTypes() -> [Any.Type] {
var ret = [Any.Type]()
for (var i = 0; i<rand()%10; i++) {
if (rand()%2 == 0){ ret.append(Int.self) }
else {ret.append(String.self) }
}
return ret
}
func test() {
var ts = self.someTypes()
for t in ts {
var arr = Array<t>()
}
}
Swift's static typing means the type of a variable must be known at compile time.
In the context of a generic function func foo<T>() { ... }, T looks like a variable, but its type is actually known at compile time based on where the function is called from. The behavior of Array<T>() depends on T, but this information is known at compile time.
When using protocols, Swift employs dynamic dispatch, so you can write Array<MyProtocol>(), and the array simply stores references to things which implement MyProtocol — so when you get something out of the array, you have access to all functions/variables/typealiases required by MyProtocol.
But if t is actually a variable of kind Any.Type, Array<t>() is meaningless since its type is actually not known at compile time. (Since Array is a generic struct, the compiler needs know which type to use as the generic parameter, but this is not possible.)
I would recommend watching some videos from WWDC this year:
Protocol-Oriented Programming in Swift
Building Better Apps with Value Types in Swift
I found this slide particularly helpful for understanding protocols and dynamic dispatch:
There is a way and it's called generics. You could do something like that.
class func foo() {
test(Int.self)
}
class func test<T>(t: T.Type) {
var arr = Array<T>()
}
You will need to hint the compiler at the type you want to specialize the function with, one way or another. Another way is with return param (discarded in that case):
class func foo() {
let _:Int = test()
}
class func test<T>() -> T {
var arr = Array<T>()
}
And using generics on a class (or struct) you don't need the extra param:
class Whatever<T> {
var array = [T]() // another way to init the array.
}
let we = Whatever<Int>()
jtbandes' answer - that you can't use your current approach because Swift is statically typed - is correct.
However, if you're willing to create a whitelist of allowable types in your array, for example in an enum, you can dynamically initialize different types at runtime.
First, create an enum of allowable types:
enum Types {
case Int
case String
}
Create an Example class. Implement your someTypes() function to use these enum values. (You could easily transform a JSON array of strings into an array of this enum.)
class Example {
func someTypes() -> [Types] {
var ret = [Types]()
for _ in 1...rand()%10 {
if (rand()%2 == 0){ ret.append(.Int) }
else {ret.append(.String) }
}
return ret
}
Now implement your test function, using switch to scope arr for each allowable type:
func test() {
let types = self.someTypes()
for type in types {
switch type {
case .Int:
var arr = [Int]()
arr += [4]
case .String:
var arr = [String]()
arr += ["hi"]
}
}
}
}
As you may know, you could alternatively declare arr as [Any] to mix types (the "heterogenous" case in jtbandes' answer):
var arr = [Any]()
for type in types {
switch type {
case .Int:
arr += [4]
case .String:
arr += ["hi"]
}
}
print(arr)
I would break it down with the things you already learned from the first answer. I took the liberty to refactor some code. Here it is:
func someTypes<T>(t: T.Type) -> [Any.Type] {
var ret = [Any.Type]()
for _ in 0..<rand()%10 {
if (rand()%2 == 0){ ret.append(T.self) }
else {
ret.append(String.self)
}
}
return ret
}
func makeArray<T>(t: T) -> [T] {
return [T]()
}
func test() {
let ts = someTypes(Int.self)
for t in ts {
print(t)
}
}
This is somewhat working but I believe the way of doing this is very unorthodox. Could you use reflection (mirroring) instead?
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}