Mateo Villagomez Mateo Villagomez - 6 months ago 10
Swift Question

Function returning specified values but structure doesn't append its values

https://github.com/mateo951/ISBN-Vista-Jera-rquica- Github Link
The structure I have is supposed to be appending values after an internet search. The internet search is called within a function and returns two strings and an image. When I try to append the returned values in the structure, the image is saved but strings are nil.

var datosLibros = [bookData]()

@IBAction func Search(sender: UITextField) {
let (title1, author1, cover1) = (internetSearch(sender.text!))
let libro = bookData(title: title1, author: author1,image:cover1)
datosLibros.append(libro)
print(datosLibros)
}


The saved structured that is printed to the console is the following:

bookData(title: "", author: "", image: <UIImage: 0x7f851a57fbf0>, {0, 0})


Structure:

struct bookData {
var title: String
var author: String
var image: UIImage
init(title: String, author: String, image: UIImage) {
self.title = title
self.author = author
self.image = image
}


}

Thanks in advanced for any advice of help provided. I'm new to swift so there are a lot of stuff uncovered.

Answer

The problem is not with the code you posted but with internetSearch.

But before I explain what is going on there, just a quick note about Swift structs. Structs come with one free initializer that takes as its parameters one value for each stored property defined on the struct. Argument labels correspond to the variable labels.

So for your struct bookData (which really should be BookData since types should be capitalized), you do not need to include that initializer you wrote because it will be automatically provided for you as long as you do not create any additional BookData initializers.

Now for the reason your results are not what you expect. Your Strings are not coming back as nil. Instead, they are coming back as empty Strings, or "". In Swift, "" is very different from nil, which means a complete absence of a value. So your Strings are indeed there, they are just empty.

Okay, our Strings are coming back empty. How about our image? No, our image is not coming back either. You thought it was because you saw a UIImage reference printed in the console, but if you look closer you will notice it is a bogus image. Notice "{0, 0}" after the memory address for the instance. As far as I'm aware, this means the image has a size of 0 x 0. How many useful images do you know that have a size of 0 x 0?

So now we have discovered that our Strings are coming back empty and effectively so is our image. What is going on here?

Well, in your implementation of internetSearch I found on GitHub, this is the first thing you do:

var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()

Naturally, you do this so that you have some variables of the correct types ready to plop in some actual results if you find them. Just for fun, let's see what the result of the code above would be if there were no results.

Well, the initializer for String that accepts no parameters results in an empty String being created.

Okay, how about our image. While the documentation for UIImage does not even mention an initializer that takes no parameters, it does inherit one from NSObject and it turns out that it will just create an empty image object.

So we now have discovered that what internetSearch is returning is actually the same as what it would be if there were no results. Assuming you are searching for something that you know exists, there must be a problem with the search logic, right? Not necessarily. I noticed that your implementation of the rest of internetSearch relies on an NSURLSession that you use like so:

var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()

let session = NSURLSession.sharedSession()

let task = session.dataTaskWithURL(url) { (data, response, error) -> Void in

// Lots of code that eventually sets the three variables above to a found result

}

task.resume()

return (bookTitle, bookAuthor, bookCover)

That seems fine and dandy, except for the fact that NSURLSession performs its tasks asynchronously! Yes, in parts you even dispatch back to the main queue to perform some tasks, but the closure as a whole is asynchronous. This means that as soon as you call task.resume(), NSURLSession executes that task on its own thread/queue/network and as soon as that task is set up it returns way before it completes. So task.resume() returns almost immediately, before any of your search code in the task actually runs, and especially before it completes.

The runtime then goes to the next line and returns those three variables, just like you told it to. This, of course, is the problem because your internetSearch function is returning those initial empty variables before task has a chance to run asynchronously and set them to helpful values.

Suggesting a fully-functional solution is probably beyond the scope of this already-long answer, but it will require a big change in your implementation detail and you should search around for using data returned by NSURLSession.

One possible solution, without me posting any code, is to have your internetSearch function not return anything, but on completion of the task call a function that would then append the result to an array and print it out, like you show. Please research this concept.

Also, I recommend changing your implementation of internetSearch further by declaring your initial values not as:

var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()

…but as:

var bookTitle: String?
var bookAuthor: String?
var bookCover: UIImage?

This way, if you find a result than you can represent it wrapped in an Optional and if not you can represent that as nil, which will automatically be the default value of the variables in the code directly above.

Comments