Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift/iOS: How to use inout parameters in functions with AnyObject/Any or Pointers

I'm trying to write a function that takes a variable pointer and a descriptor/key and sets a new value for the variable. Ideally the pointer should be either of an object or a primitive, but I could also live with separate functions (or an additional parameter). In my code I retrieve the new value from a database also using the key, but in the following example I simplified it with dummy values so that it can be used easily in a playground:

import UIKit

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "String":
        object = "A String"
    case "UIColor":
        object = UIColor.whiteColor()
    case "Bool":
        object = true
    default:
        println("Unhandled key: \(key)")
    }
}

var string: String = "Default String"
var color: UIColor = UIColor.blackColor()
var bool: Bool = false

setValue(&string, "String")
setValue(&color, "UIColor")
setValue(&bool, "Bool")

I get the following error:

"Cannot invoke 'setValue' with an argument list of type '(inout String, key String)'"

I understand that I'm mixing Objects and Primitives here. I also tried to break it down and separate these into two functions, but even that fails:

func setValue(inout object: AnyObject, key: String) {
    switch key {
    case "UIColor":
        object = UIColor.whiteColor()
    default:
        println("Unhandled key: \(key)")
    }
}

var color: UIColor = UIColor.blackColor()
setValue(&color, "UIColor")

This also gives the same error:

"Cannot invoke 'setValue' with an argument list of type '(inout UIColor, key String)'"

If I change 'AnyObject' to 'UIColor' it works, but the point of the function is, that it takes any variable type or at least any object type (I'd write a second function using "Any" for primitives then or add another parameter)

In Objective-C I was using pointers, transferring the approach to Swift also doesn't work, same result:

func setValue(object: UnsafeMutablePointer<AnyObject>, key: String) {
    switch key {
    case "String":
        object.memory = "A String"
    case "UIColor":
        object.memory = UIColor.whiteColor()
    case "Bool":
        object.memory = true
    default:
        println("Unhandled key: \(key)")
    }
}

Does anybody have an idea what I'm missing here? Any help is much appreciated!

Thanks!

like image 304
Django Avatar asked Apr 30 '15 09:04

Django


People also ask

What is a good use case for an inout parameter in Swift?

A good use case will be swap function that it will modify the passed-in parameters. Swift 3+ Note: Starting in Swift 3, the inout keyword must come after the colon and before the type. For example, Swift 3+ now requires func changeChar(char: inout Character) .

How does Inout work in Swift?

Swift inout parameter is a parameter that can be changed inside the function where it's passed into. To accept inout parameters, use the inout keyword in front of an argument. To pass a variable as an inout parameter, use the & operator in front of the parameter.

What is Inout parameter?

If you want, you can pass in one or more parameters as inout , which means they can be changed inside your function, and those changes reflect in the original value outside the function.

How are parameters passed in Swift?

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's passed in to the function, is modified by the function, and is passed back out of the function to replace the original value.


2 Answers

Better you can create a generic method like below:

func setValue<T>(inout object:T, key: String) {
    switch key {
    case "String":
        object = ("A String" as? T)!
    case "UIColor":
        object = (UIColor.whiteColor() as? T)!
    case "Bool":
        object = (true as? T)!
    default:
        println("Unhandled key: \(key)")
    }
}

And calling will be like this:

setValue(&string, key: "String")
setValue(&color, key: "UIColor")
setValue(&bool, key: "Bool")

Hope it helps!

like image 159
Sohil R. Memon Avatar answered Oct 06 '22 01:10

Sohil R. Memon


The right way to do this is to use overloading, and letting the compiler choose the appropriate bit of code at compile time, instead of switching off a string at runtime:

func setValue(inout object: String) {
    object = "A String"
}

func setValue(inout object: UIColor) {
    object = UIColor.whiteColor()
}

func setValue(inout object: Bool) {
    object = true
}

func setValue(inout object: Any) {
    println("Unhandled key: \(key)")
}

This approach wouldn’t work when you have an Any and you want to indicate to the function what type is contained in the Any… but in this case, the reason you have problems is that the compiler does know what the types are, so you can take advantage of that.

like image 41
Airspeed Velocity Avatar answered Oct 06 '22 00:10

Airspeed Velocity