'var' parameters are deprecated and will be removed in Swift 3
Asked Answered
A

9

132

Alright so I just update Xcode to 7.3 and now I get this warning:

'var' parameters are deprecated and will be removed in Swift 3

How to fix this when I need to use the var in this function:

public func getQuestionList(var language: String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}
Artillery answered 22/3, 2016 at 20:52 Comment(8)
How about public func getQuestionList(inout language: String) -> NSArrayHairworm
No, this is not a suitable replacement. OP probably does not want getQuestion to have any side effects.Delate
I honestly have no idea why they would even consider removing this. It was one of the features that made swift awesome!Icelander
Never used it myself and don't understand the fuss.Albers
@MikeTaverne (late reply) Consider the following function: func foo(_ bar: int) { /*use bar*/ bar+=1; foo(bar); }. This is impossible without var params. You either need to create a separate var within the function and copy the value, or mark the param as inout. The former is slow, the latter causes undefined behaviour. Many algorithms use recursion like this.Leiva
@Leiva In your function, why not just call foo(bar + 1)?Albers
@MikeTaverne Because this is a simple example, and what you suggest is not always an option. What if bar is not an int but a complex object?Leiva
@Leiva What makes you think an intermediary variable like that would not be optimized away? Or what do you mean by "slow"?Ferdinandferdinanda
S
86

Have you tried to assign to a new var

public func getQuestionList(language: String) -> NSArray {
    var lang = language
    if self.data.count > 0 {
        if (lang.isEmpty) {
            lang = "NL"
        }
        return self.data.objectForKey("questionList" + lang) as! NSArray
    }

    return NSArray()
}
Schwinn answered 22/3, 2016 at 21:1 Comment(13)
Not really what I think the OP wantedAmblygonite
I would have understood OP's question the same way as @garana. OP doesn't use inout in their question, they just mutate a pre-existing variable locally.Forthwith
Totally agree, so I don't know why @Amblygonite downvote my answer. The OP question is about mutate a value.Schwinn
@Schwinn At this time you have 3 downvotes and 2 upvotes. Please don't assume who did downvote what. You can't know. // This Q&A is just a mess. It happens. :)Forthwith
Actually this is the correct solution. Please see the Swift evolution issue that proposed this change: github.com/apple/swift-evolution/blob/master/proposals/…Starfish
While this answers OP's question, a default parameter value would be a much better solution.Dendroid
@TomSawyer You knew what you were getting into with Swift. If you don't want to use an evolving language, go with Objective-C.Dendroid
@TimVermeulen Everyone want to use a progressive language. Apple can develop their language by many ways not by changing the syntax every single month. As you know, a ton of online document and code snippet have been expiring or being outdated because of Apple. Developers have to come to this site to ask for help with many stupid questions repeatedly because of it. Syntax must be solid from the beginning if Apple want more developers to be good at it.Toner
@Toner Syntax changes have been really minor so far. Besides, programming has always been more about a deep level of understanding, rather than about basic syntax. Nobody forces you to use Swift. Apple changes the Swift syntax in order to improve the language, and I think they're doing a great job so far.Dendroid
How can this be the solution when the swift evolution document says: However, shadowing is not necessarily an ideal fix and may indicate an anti-pattern. We expect users of Swift to rethink some of their existing code where these are used but it is not strictly necessary to react to this language change. ?Aaren
Use var language = language, if you don't want to introduce another variable name (which was the main advantage of the var parameter in the first place imo)Adventurism
@Harris this was a terrific suggestion. you should post as an answer because others may be using var parameters for this reason. your solution is the best approach since it doesn't require introducing a new variable name.Lighterman
@Toner if Apple did that, nothing would ever improve in the language. See what happened to Java, it takes decades for modern language features to make it into it because they can't break backwards compatibility. For Swift, old binaries compiled with 2.2 or even 1.x will still continue to work, and you can still use these old versions. It's a major release for a reason.Stahl
M
109

The discussion of the removal of Var from a function parameter is fully documented within this submission on GitHub: Remove Var Parameters

In that document you will find that people often confuse var parameters with inout parameters. A var parameter simply means that the parameter is mutable within the context of the function, while with an inout parameter the value of the parameter at the point of return will be copied out of the function and into the caller's context.

The correct way to solve this problem is to remove var from the parameter and introduce a local var variable. At the top of the routine copy the parameter's value into that variable.

Mirellamirelle answered 22/3, 2016 at 21:33 Comment(9)
I don't understand this change at all, why would having to write another line to create a mutable local var be better than just defining the param as a var?Ascospore
For me this change is good because it is picking up situations where I should have implemented a local variable but I didn't because I took the easy way out and accepted (old) Swift's suggestion of making the input parameter a varAmber
I'm with @RossBarbish on this. So... this is being removed because lazy developers can't distinguish between inout and var parameters? Pfff...Icelander
This seems awfully unnecessary..., they should have kept both options.Sumikosumma
Really awful solution. It's logical that a var function parameter doesn't write back to the original input one. It's not that hard to understand it's not a inout thing. However, now I have to write a extra line with var name = name, just to be able to mutate the parameter inside the function. Something that would happen quite often I think.Scevor
Probably swift was declaring a local variable overtop of the parameter behind the scenes anyway. Now we have to do it manually. No change in performance, but we lost convenience to help beginners with a simple concept.Tilburg
This is exactly why I started hating Scala, it has the same problem. This is an awful decision made by Apple.Traitor
@hey_you scala allows var parametersAurie
@javadba it does not allow var parameters in functions, only vals.Traitor
S
86

Have you tried to assign to a new var

public func getQuestionList(language: String) -> NSArray {
    var lang = language
    if self.data.count > 0 {
        if (lang.isEmpty) {
            lang = "NL"
        }
        return self.data.objectForKey("questionList" + lang) as! NSArray
    }

    return NSArray()
}
Schwinn answered 22/3, 2016 at 21:1 Comment(13)
Not really what I think the OP wantedAmblygonite
I would have understood OP's question the same way as @garana. OP doesn't use inout in their question, they just mutate a pre-existing variable locally.Forthwith
Totally agree, so I don't know why @Amblygonite downvote my answer. The OP question is about mutate a value.Schwinn
@Schwinn At this time you have 3 downvotes and 2 upvotes. Please don't assume who did downvote what. You can't know. // This Q&A is just a mess. It happens. :)Forthwith
Actually this is the correct solution. Please see the Swift evolution issue that proposed this change: github.com/apple/swift-evolution/blob/master/proposals/…Starfish
While this answers OP's question, a default parameter value would be a much better solution.Dendroid
@TomSawyer You knew what you were getting into with Swift. If you don't want to use an evolving language, go with Objective-C.Dendroid
@TimVermeulen Everyone want to use a progressive language. Apple can develop their language by many ways not by changing the syntax every single month. As you know, a ton of online document and code snippet have been expiring or being outdated because of Apple. Developers have to come to this site to ask for help with many stupid questions repeatedly because of it. Syntax must be solid from the beginning if Apple want more developers to be good at it.Toner
@Toner Syntax changes have been really minor so far. Besides, programming has always been more about a deep level of understanding, rather than about basic syntax. Nobody forces you to use Swift. Apple changes the Swift syntax in order to improve the language, and I think they're doing a great job so far.Dendroid
How can this be the solution when the swift evolution document says: However, shadowing is not necessarily an ideal fix and may indicate an anti-pattern. We expect users of Swift to rethink some of their existing code where these are used but it is not strictly necessary to react to this language change. ?Aaren
Use var language = language, if you don't want to introduce another variable name (which was the main advantage of the var parameter in the first place imo)Adventurism
@Harris this was a terrific suggestion. you should post as an answer because others may be using var parameters for this reason. your solution is the best approach since it doesn't require introducing a new variable name.Lighterman
@Toner if Apple did that, nothing would ever improve in the language. See what happened to Java, it takes decades for modern language features to make it into it because they can't break backwards compatibility. For Swift, old binaries compiled with 2.2 or even 1.x will still continue to work, and you can still use these old versions. It's a major release for a reason.Stahl
A
67

Just add this one line at the beginning of the function:

var language = language

and the rest of your code can stay unchanged, like this:

public func getQuestionList(language: String) -> NSArray {
    var language = language
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}
Adventurism answered 31/3, 2016 at 3:2 Comment(10)
The best answer by far. Only requires changing one line.Delate
But looks so unnatural @JamesQuaggy
I feel this is the best answer as it keeps the same name. Similar to how other common languages does it.Ahoufe
I upvoted this answer because it minimizes code changes, but I could easily imagine this kind of identifier reuse being disallowed in a future version of Swift.Bowhead
This might be confusing to other developers that work on your project. Although the line explains what it's doing, it doesn't explain why you had to do it. I would recommend adding the Swift Evolution number (SE-0003) as a comment.Agnola
@RiverSatya Why not just use the parameter directly?Timothytimour
The parameter is immutable. The new variable can be modified.Aglaia
Really an awesome suggestion. We will implement it this way in Swiftify :)Clastic
Yes, that's the solution I always use. But it's annoying and ugly.Pevzner
@IanLovejoy This kind of identifier reuse will always be allowed. Shadowing is a critical part of Swift, especially in if let and guard let statements.Delate
D
14

A lot of people are suggesting an inout parameter, but that's really not what they're designed for. Besides, it doesn't allow calling the function with a let constant, nor with a string literal. Why don't you simply add the default value to the function signature?

public func getQuestionList(language language: String = "NL") -> NSArray {
    if data.count > 0 {
        return data.objectForKey("questionList" + language) as! NSArray
    } else {
        return NSArray()
    }
}

Just make sure to not call getQuestionList with the empty string in case you want the default language, but just leave out the parameter:

let list = getQuestionList() // uses the default "NL" language
Dendroid answered 22/3, 2016 at 21:13 Comment(2)
I also don't understand why everybody jumped on the inout solution when OP wasn't even using that in the beginning...Forthwith
They were assuming that var and inout did the same thing.Homology
L
1
public func getQuestionList(language: inout String) -> NSArray {
if self.data.count > 0 {
    if (language.isEmpty) {
        language = "NL"
    }
    return self.data.objectForKey("questionList" + language) as! NSArray
}

return NSArray()

}

Lurcher answered 4/1, 2018 at 17:51 Comment(1)
code.tutsplus.com/tutorials/…Lurcher
A
0

I think @Harris and @garanda answers are the best approach.

Anyway in your case, there isn't need of a var, you can do:

public func getQuestionList(language: String) -> NSArray {
    if self.data.count > 0 {
        return self.data.objectForKey("questionList" + (language.isEmpty ? "NL" : language)) as! NSArray
    }
    return NSArray()
}
Archaeopteryx answered 14/6, 2016 at 16:35 Comment(0)
A
0

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

In-Out Parameters

Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.

You write an in-out parameter by placing the inout keyword right before a parameter’s type. An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. For a detailed discussion of the behavior of in-out parameters and associated compiler optimizations, see In-Out Parameters.

You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified. You place an ampersand (&) directly before a variable’s name when you pass it as an argument to an in-out parameter, to indicate that it can be modified by the function.

NOTE

In-out parameters cannot have default values, and variadic parameters cannot be marked as inout.

Here’s an example of a function called swapTwoInts(::), which has two in-out integer parameters called a and b:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

The swapTwoInts(::) function simply swaps the value of b into a, and the value of a into b. The function performs this swap by storing the value of a in a temporary constant called temporaryA, assigning the value of b to a, and then assigning temporaryA to b.

You can call the swapTwoInts(::) function with two variables of type Int to swap their values. Note that the names of someInt and anotherInt are prefixed with an ampersand when they are passed to the swapTwoInts(::) function:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

The example above shows that the original values of someInt and anotherInt are modified by the swapTwoInts(::) function, even though they were originally defined outside of the function.

NOTE

In-out parameters are not the same as returning a value from a function. The swapTwoInts example above does not define a return type or return a value, but it still modifies the values of someInt and anotherInt. In-out parameters are an alternative way for a function to have an effect outside of the scope of its function body.

Affiliation answered 31/1, 2017 at 16:3 Comment(0)
H
0

Here is another idea. My use case was to pass around a string array to append to it, for which the array must be passed in mutably. I did not want to have state in my class for this either. So I made a class that holds the array and pass that. Depending on your use case it may seem silly to have a class that holds just that one variable.

private class StringBuilder {
    var buffer: [String] = []

    func append(_ str: String) {
        buffer.append(str)
    }

    func toString() -> String {
        return buffer.joined()
    }
}

I only use append and joined methods on the array so it was easy to change the type with minimal other changes to my code.

Some example usage:

private func writeMap(map: LevelMap, url: URL) -> Bool {
    let buffer = StringBuilder()

    if !writeHeader(map: map, buffer: buffer) {
        return false
    }
    if !writeFloors(map: map, buffer: buffer) {
        return false
    }

    let content = buffer.toString()
    do {
        try content.write(to: url, atomically: true, encoding: .utf8)
        return true
    } catch {}
    return false
}

private func writeHeader(map: LevelMap, buffer: StringBuilder) -> Bool {
    buffer.append("something here ...\n")
    return true
}
Herakleion answered 12/2, 2017 at 1:52 Comment(1)
My answer is if you WANT the original value as seen by the caller to be modified. If you just wanted to be able to locally reassign the value but not have it affect the caller, other answers above deal with that.Herakleion
J
-1

I think you should inout instead, following the recommendations given in this discussion.

To put it in context, in your example:

public func getQuestionList(var language: String) 

should be replaced by:

public func getQuestionList(language: inout String) 
Ja answered 29/3, 2023 at 9:30 Comment(2)
The first 2 comments to the question explain why this can be a bad fix: any changes made to the variable ('language' here) inside the func in the past did NOT affect the variable in the calling routine -- by design with the var keyword, whereas with inout they do. In a world where both var and inout exist, and the choice was made to use one, just swapping in the other will cause things to work not-as-intended. If the var keyword is unavailable, then there's a choice: use inout or the 'var lang = language' approach, depending on the desired scope of changes.Mancy
Well, I’m not talking about refactoring but more about using it as the desired behavior.Ja

© 2022 - 2024 — McMap. All rights reserved.