I need a function that can return either a String
or an Int
depending on the parameters entered eg:
func getValue (type: String) -> (String || Int) { //this line is obviously wrong
if type == "type1" {
return "exampleString"
}
else if type == "type2"
return 56
}
}
Use of returnA function can not return multiple values, but similar results can be obtained by returning an array.
We can return multiple values from a swift function. It can be achieved by returning a tuple. Using a tuple, we can group multiple values of different types and we can return that. Each element can be accessed by its name.
Swift's functions have a single return type, such as Int or String , but that doesn't mean we can only return a single value. In fact, there are two ways we can send back multiple pieces of data: Using a tuple, such as (name: String, age: Int) Using some sort of collection, such as an array or a dictionary.
If you want to return your own value from a function, you need to do two things: Write an arrow then a data type before your function's opening brace, which tells Swift what kind of data will get sent back. Use the return keyword to send back your data.
Edit: I'm editing this answer, because I think my old answer is mostly just leading people down the wrong road.
The ability to return one of two (completely-unrelated) types might sound like it would solve your problem, but it almost certainly does not. Swift is a statically typed language, which means that it limits what you can do to a value, depending on its type (this limitation is beneficial, because you know this will always work!).
You can multiply Int
s, and you can concatenate String
s, but you can't multiply String
s or concatenate Int
s. But what if you had a value that was either a hypothetical (Int || String)
? What could you do with that?
Well you can't multiply, because what if it was a String
underneath? That wouldn't make sense.
You couldn't concatenate it either, because what if the underlying value was an Int
? That wouldn't make sense, either.
The only thing that would be allowable, is to only do those things which are supported by both Int or String. And what is that exactly? Well, you could get the description: String
, and that's... about it.
The modern solution to this problem is to describe the capabilities of the result using a protocol, and to return that. Here's an example:
protocol ModeOfTransport {
func transport(cargo: String)
}
struct Car: ModeOfTransport {
func transport(cargo _: String) {
print("I'll put the light cargo in my trunk and drive it.")
}
}
struct Train: ModeOfTransport {
func transport(cargo: String) {
print("I'll put attach the heavy cargo in a new car and pull it.")
}
}
func getAppropriateModeOfTransport(cargoWeight: Int) -> ModeOfTransport {
if cargoWeight < 500 {
return Car()
} else {
return Train()
}
}
let modeOfTransport = getAppropriateModeOfTransport(cargoWeight: 1234)
modeOfTransport.transport(cargo: "Sample Cargo")
You can use Enumeration
You can use an enumeration with associated values to achieve the behaviour you're looking for. They're much like a nicer version of C's unions.
enum Foo { //TODO: Give me an appropriate name.
case type1(String)
case type2(Int)
static func getValue(type: String) -> Foo {
switch (type) {
case "type1": return type1("exampleString")
case "type2": return type2(56)
default: fatalError("Invalid \"type\"");
}
}
}
let x = Foo.getValue(type: "type1")
This is actually very annoying, because the only way to do anything sensible with these values it to consume them conditionally, by switching on its type and responding accordingly:
switch x {
case .type1(let string): funcThatExpectsString(string)
case .type2(let int): funcThatExpectsInt(int)
}
If you're not careful, these switch
es will consume your entire codebase. This is why I recommend the protocol-base approach above.
I faced a similar problem and I solved in this way (you can use default associated value introduced in Swift 5.1 and opaque return type)
class PersistanceHelper {
enum PersistenceType {
case userStatus(status: String = "")
case firstAccess(isFirstAccess: Bool = true)
case biometricsEnabled(isBiometricsEnabled: Bool = true)
case notificationToken(token: String = "")
func getKey() -> String {
switch self {
case .userStatus : return "userStatusKey"
case .firstAccess. : return "firstAccessKey"
case .biometricsEnabled : return "biometricsEnabledKey"
case .notificationToken : return "notificationTokenKey"
}
}
}
static func save(_ objectType: PersistenceType) {
switch objectType {
case .userStatus(let payload), .notificationToken(let payload):
UserDefaults.standard.set(payload, forKey: objectType.getKey())
case .firstAccess(let payload), .biometricsEnabled(isBiometricsEnabled: let payload):
UserDefaults.standard.set(payload, forKey: objectType.getKey())
}
}
static func load<T>(_ objectType: PersistenceType) -> T? {
UserDefaults.standard.object(forKey: objectType.getKey()) as? T
}
}
And then use it where you need...
PersistanceHelper.save(.notificationToken(token: "93028184-87be-4a62-bcc9-70ec08d6fe7e"))
PersistanceHelper.save(.biometricsEnabled(isBiometricsEnabled: true))
if let token: String = PersistanceHelper.load(.notificationToken()),
let isBiometricEnabled: Bool = PersistanceHelper.load(.biometricsEnabled()) {
print(token)
print(isBiometricEnabled)
}
The enums with associated values allow to write a self explaining code... at least to me :D
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With