Translation: Ray16897188; translation: Ray16897188; Proofreading: PMST; Final draft: small pot

Article series address:

  • Swift programming thought, part 1: Saving the pony
  • Swift programming ideas, part 2: Array Map method

Continuing our Swift programming ideas series, today we’ll make a few small changes, using structs to simplify code even further.

So the previous part

In a previous article in this series, we learned about using Map and flatMap for Arrays, eliminating the statefulness of intermediate variables, and using some functional programming 1.

Here’s our code from the end of the day, posted here for a review:

class ListItem {
    var icon: UIImage?
    var title: String = ""
    var url: NSURL!

    static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
        guard let nonNilJsonData = jsonData,
            let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
            let jsonItems = json as? Array
            else {
                return []
        }

        return jsonItems.flatMap { (itemDesc: NSDictionary) -> ListItem? in
            guard let title = itemDesc["title"] as? String,
                let urlString = itemDesc["url"] as? String,
                let url = NSURL(string: urlString)
                else { return nil }
            let li = ListItem()
            if let icon = itemDesc["icon"] as? String {
                li.icon = UIImage(named: icon)
            }
            li.title = title
            li.url = url
            return li
        }
    }
}Copy the code

Today we’ll make a fairly simple change to the example to make the code leaner and swift-er.

Compared to the Struct Class

As the code above shows, thinking about using classes first is a common mistake Swift beginners make. But that makes sense, because in ObjC we use classes all over the world.

Using class is not an error of principle. You can certainly continue to use it in Swift. But Swift’s structs are much more powerful than their C predecessors: they no longer just store a series of values.

Swift structs and classes have the same functionality — except for inheritance — structs are value-types (so every variable assignment is a copy of the value, much like an Int). Classes are reference-types that are passed by reference rather than copied by value, as in Objective-C(and the ubiquitous ugly * in OC, which also stands for reference).

I’m not going to go into a long argument here about how structures and value types compare to classes and reference types: I strongly recommend that you read Andy Matuschak’s excellent work on this topic. I don’t need to explain myself. Andy said it better than I did.

Convert our class into a struct

In our case, using a structure seems more appropriate because it holds some values and doesn’t require any changes to them (more suitable for copying than referencing). In our case, we use it as a data source for a menu bar, and once created it is not changed, so this is a more logical scenario to use a structure.

Also, another advantage of migrating a class to a struct here is that it generates a default implicit constructor if the struct does not define a constructor for it: so we can very easily use the default constructor ListItem(icon:… Title:… , the url:…). To create a ListItem.

The last point is that since we eliminated data corruption in the previous article, the ListItem in question would not have been created, so we could remove the default “” for title, but more importantly we could remove the NSURL! Convert NSURL! To save the last pony 🐴2.

The converted code looks like this:

struct ListItem {
    var icon: UIImage?
    var title: String
    var url: NSURL

    static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
        guard let nonNilJsonData = jsonData,
            let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
            let jsonItems = json as? Array else { return [] }

        return jsonItems.flatMap { (itemDesc: NSDictionary) -> ListItem? in
            guard let title = itemDesc["title"] as? String,
                let urlString = itemDesc["url"] as? String,
                let url = NSURL(string: urlString)
                else { return nil }
            let iconName = itemDesc["icon"] as? String
            let icon = UIImage(named: iconName ?? "")
            return ListItem(icon: icon, title: title, url: url)
        }
    }
}
Copy the code

Now when everything is in place, we only create the ListItem instance in the last step, and if you don’t provide any init methods the struct will provide a default init method to pass its own parameters. You can do the same thing with the class version, but with the class we have to declare our own init.

Coalescing Operator

I also used a new trick in the above example, using?? The operator lets iconName give a default value if it is nil.

?? Operator and ObjC’s opt? The: val expression is similar, and anyone who knows it will know: opt?? Val returns its value if opt is non-empty, or val if nil. That means that if opt is T, right? Type, val must be of type T, and the result of the entire expression will also be of type T.

So here iconName?? “” will allow us to use a null character “” image name if iconName is nil, so there’s going to be a nil UIImage here, and the icon is going to be nil.

⚠️ Note ⚠️ : Treating a nil iconName and a nil UIImage as a result is not the best or most concise way to do it. Actually using a fake “” name to get an empty image looks a bit ugly and a bit deceptive. But this is to show you…? A case of operator presence… Hey, let’s save some good stuff for the next article in this series (spoiler alert: flatMap is involved again).

conclusion

That’s all for today.

In Part 3 we didn’t do much other than replace class with struct. I haven’t even touched on the difference between the two (though I don’t want you to wait too long for a new post, even though I’ve been extremely busy lately and haven’t blogged in a while).

But we finally dropped NSURL! , will save the last pony 🎉. Before my next post, check out Andy’s excellent discussion of Making Friends with Value Types. You still have a lot to learn.

I promise I won’t keep you waiting too long before releasing Part 4, which again touches on maps and Flatmaps, but this time based on Optionals.

  1. Yes, you did do some functional programming in the last article… You may not even be aware of it.

  2. NSURL! I’ve been pestering me for a while now, but I’ve just been too lazy to write a proper init method for the ListItem class. Knowing that I would get rid of it sooner or later, I didn’t want to pick up the code sample before disposing of it. It’s only a matter of time before we save the last pony.

This article is translated by SwiftGG translation team and has been authorized to be translated by the authors. Please visit swift.gg for the latest articles.