0

I'm having difficulty using an attribute of a class, which stores a url as a String. It's returning Optional("url") instead of just the url. The other string attribute User.name is not having that same issue. I've tried unwrapping the attibute when I'm accessing it, but it's not working because it's telling me that it "Cannot force unwrap value of non-optional type 'String'"

See below for details

I've created a class, which looks like

import Foundation

class User {
    var name : String
    var age : Double
    var gender : String
    var imagelink : String

    init(name: String, age: Double, gender: String, imagelink: String) {
        self.name = name
        self.age = age
        self.gender = gender
        self.imagelink = imagelink
    }
}

The users are listen in a table, and when a cell is pressed, it segues into a new view where I want the user's profile information and image to be displayed.

The prepareforsegue is set up like this

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "userCellPressed" {
        if let indexPath = tableView.indexPathForSelectedRow {
            let user = userList[indexPath.row]
            let controller = (segue.destinationViewController as! ProfileViewController)
            controller.detailUser = user
            controller.navigationItem.leftItemsSupplementBackButton = true
        }
    }
}

Then in the new view, the user's name is displayed correctly, but the image won't display because detailUser.imagelink is returning Optional("url") and it's not allowing me to use ! or ? on it because it's a string.

var detailUser: User? {
    didSet {
        configureView()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
    // Do any additional setup after loading the view.
}

func configureView() {
    if let detailUser = detailUser {
        if let nameLabel = nameLabel, userImage = userImage {
            nameLabel.text = detailUser.name                

            let url = NSURL(string: detailUser.imagelink)
            let data = NSData(contentsOfURL:url!)
            if data != nil {
                userImage.image = UIImage(data:data!)
            }
        }
    }
}
FortuneFaded
  • 1,259
  • 4
  • 20
  • 28

1 Answers1

3

you must unwrap url before using it. following your syntax would look like this:

func configureView() {
    if let detailUser = detailUser {
        if let nameLabel = nameLabel, userImage = userImage {
            nameLabel.text = detailUser.name                

            if let url = NSURL(string: detailUser.imagelink) {
                let data = NSData(contentsOfURL:url)
                if data != nil {
                    userImage.image = UIImage(data:data!)
                }
            }
        }
    }
}

for more readable code, i would recommend using guards (not tested):

UPDATED WITH ASYNC REQUEST:

func configureView() {
    // set name label
    guard let detailUser = detailUser else { return }
    guard let nameLabel = nameLabel else { return }
    nameLabel.text = detailUser.name

    // get weak copy of self to prevent retain cycle
    weak var weakSelf = self

    // retrieve and set user image
    guard let url = NSURL(string: detailUser.imagelink) else { return }
    NSURLSession.sharedSession().dataTaskWithRequest(NSURLRequest(URL: url), completionHandler: { data, response, error in
        // only carry on if self still exists and data was returned
        guard let strongSelf = weakSelf else { print("self released before response."); return }
        guard let data = data else { print("error retrieving image: \(error)"); return }

        // if userImage exists parse the data as an UIImage and set it
        strongSelf.userImage?.image = UIImage(data: data)
    }).resume()
}
Casey
  • 6,531
  • 24
  • 43
  • Thank you for teaching me about guards, I switched to the second code that you posted, and I'm still having the same issue. If i put in print(url) after the guard let url line, I'm getting an optional wrapped string for the url – FortuneFaded Feb 23 '16 at 01:27
  • see updated code. if ```imagelink``` is optional (can't tell from the code you pasted) then you need to unwrap it. – Casey Feb 23 '16 at 01:30
  • @FortuneFaded NSData(contentsOfURL:) should only be used for local resource fileURL. There is no guarantee the data will be available. – Leo Dabus Feb 23 '16 at 01:31
  • You should use NSURLSession downloadTaskWithURL – Leo Dabus Feb 23 '16 at 01:32
  • ^ that is true! i was assuming your imagelink was a local file path – Casey Feb 23 '16 at 01:32
  • @Casey no need to use multiple guards just use a comma between them. You can also omit the let keyword if you don't have a where clause at the previous conditional – Leo Dabus Feb 23 '16 at 01:34
  • Ah ok, gosh learning so much from this one comment section, you guys are awesome. @Casey I used the guard let imageLink line, but it's giving me an error saying that "Initializer for conditional binding must have Optional type, not String. LeoDabus yes, it's a web URL, so I will switch to NSURLSession, thank you. – FortuneFaded Feb 23 '16 at 01:53
  • updated code for asynchronously retrieving the remote image and setting it – Casey Feb 23 '16 at 01:59
  • @Casey thank you for all your help. From a comment above, I realized that the issue was occurring when I was initializing the User class. I did not unwrap at that time, so it was saving the string as "Optional(xxxxx)". I've learned a lot from this answer though, so I appreciate all your help – FortuneFaded Feb 23 '16 at 02:07