Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift tuple to Optional assignment

I am writing some code in Swift to learn the language. Here is my base class:

import Foundation
class BaseCommand:NSOperation
{
    var status:Int? = nil
    var message:String? = nil

    func buildRequest() -> NSData?
    {
        return nil
    }

    func parseResponse(data:NSData?) -> (Status:Int, Error:String)
    {
        return (200, "Success")
    }

    override func main() {
        let requestBody = self.buildRequest()

        println("Sending body \(requestBody)")
        // do network op
        var networkResultBody = "test"

        var resultBody:NSData = networkResultBody.dataUsingEncoding(NSUTF8StringEncoding)!
        (self.status, self.message) = self.parseResponse(resultBody)
    }
}

The problem is on the last line:

(self.status, self.message) = self.parseResponse(resultBody)

The compiler says "Cannot express tuple conversion (Status:Int, Error:String) to (Int?, String?)"

I understand that the issue is that self.status and self.message are optionals, and the parseResponse does not return Optionals (and I don't want it to). How do I tell it to do the necessary assign and convert to get the data into the instance variables?

like image 702
David S. Avatar asked Aug 24 '15 02:08

David S.


2 Answers

Another answer suggested (before it was changed) to just do:

(self.status!, self.message!) = self.parseResponse(resultBody)

I have found that is unsafe. It will crash if either self.status or self.message is nil at the time of the assignment. Try this test in a Playground:

class Test {
    var status: Int?
    var error: String?

    func parseResponse() -> (Status:Int, Error:String)
    {
        return (200, "Success")
    }

    func testIt() {
        (self.status!, self.error!) = parseResponse()
        print(self.status)
        print(self.error)
    }
}

let mytest = Test()
mytest.testIt()

Here is another way it could be done:

let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (Optional(stat), Optional(err))

or, as @AndriyGordiychuk discovered, it works without Optional:

let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (stat, err)

It's curious that that works, but assigning the result of the function does not.


Note the following experiment:

var i: Int?
var s: String?

// This works
(i, s) = (3, "hello")

// This fails with error: cannot express tuple conversion '(Int, String)' to '(Int?, String?)
let t = (3, "hello")
(i, s) = t

It seems that when the assignment is a tuple literal assigned to a tuple, Swift takes a shortcut and doesn't first construct the tuple. Instead, is just assigns the individual elements.

So this:

(i, s) = (3, "hello")

is equivalent to:

i = 3
s = "hello"

which works because you can assign an Int to an Int? variable and a String to a String? variable. The tuple assignment fails because the types need to match.

like image 105
vacawama Avatar answered Oct 19 '22 14:10

vacawama


You have to do this (no optionals needed):

let returnValue = self.parseResponse(resultBody)
(self.status,self.message) = (returnValue.Status,returnValue.Error)
like image 3
Andriy Gordiychuk Avatar answered Oct 19 '22 14:10

Andriy Gordiychuk