Refactoring Code in Swift

I finally got sometime to refactor the code of my App Leetcoder. The followings are a few things I deem worth writing down.

Fetching Data With Async Requests

Code like the snippet below is not only ugly but also unresponsive: it blocks the user interface.

1
2
let data = NSData(contentsOfURL: NSURL(string: url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!)!)
var posts = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! [Dictionary<String, AnyObject>]

I have changed much of that into async requests a while back but I finally got the time to change all of that.

Now all my code uses Alamofire to fetch JSON data instead.

1
2
3
4
5
Alamofire.request(.GET, url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!).responseJSON { response in
if let JSON = response.result.value {
self.relatedPosts = JSON as! [Dictionary<String, AnyObject>]
}
}

It may not seem necessary to some if the size of the data to be fetched is small or when there is nothing to do anyways before any data can be loaded. However, slow network happens and showing a loading screen is much more conforting than showing nothing at all.

Property Observers

Swift’s property observer is a great way to put code in the relevant place. You should use it if something needs to happen whenever a property changes.

For example, when you display an array of data in a tableview, you probably want to reload the table whenever the array changes. That’s super easy to accomplish with property observers in Swift.

1
2
3
4
5
var nameArray: [String] {
didSet {
self.tableView.reloadData()
}
}

Extensions

The snippet below is a little unpleasant… It’s mainly because that to pass a string to NSURL, you need to encode it first. And the way to encode it involves two long names.

1
let data = NSData(contentsOfURL: NSURL(string: url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!)!)`

Luckily, you can hide this little detail by using an extension:

1
2
3
4
5
extension String {
var encodedURLString: String {
return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
}
}

In this way, you will be able to just write

1
2
3
4
5
Alamofire.request(.GET, url.encodedURLString).responseJSON { response in
if let JSON = response.result.value {
self.relatedPosts = JSON as! [Dictionary<String, AnyObject>]
}
}

That’s much better, isn’t it?

I appreciate extension in Swift a lot and have seen many neat usages in other people’s code. When used properly, it makes your code cleaner and much more readable.

Singleton

A common theme in my app is waiting for data to be returned from network requests. I used NVActivityIndicatorView to display a nice loading screen but I configured an instance in each view controller that I needed it. Therefore, I got some unpleasant duplicate code in the project. Since the loading screen should be consistent across the entire app anyways, I can just use a singleton to do that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class LeetHUD: NSObject {
static let sharedInstance = LeetHUD()
var indicatorHW = 25.0
var indicatorPadding = 10
var indicator: NVActivityIndicatorView!
class func showActivityIndicatorInView(view: UIView) {
let frame = CGRect(x: view.frame.width/2.0 - CGFloat(self.sharedInstance.indicatorHW), y: view.frame.height/2.0 - CGFloat(self.sharedInstance.indicatorHW), width: CGFloat(self.sharedInstance.indicatorHW)*2, height: CGFloat(self.sharedInstance.indicatorHW)*2)
self.sharedInstance.indicator = NVActivityIndicatorView(frame: frame, type: .LineScale, color: UIColor(red: 245.0/255.0, green: 120.0/255.0, blue: 34.0/255.0, alpha: 1.0))
self.sharedInstance.indicator.padding = CGFloat(self.sharedInstance.indicatorPadding)
view.addSubview(self.sharedInstance.indicator)
self.sharedInstance.indicator.startAnimation()
}
class func hideActivityIndicator() {
self.sharedInstance.indicator.stopAnimation()
self.sharedInstance.indicator.removeFromSuperview()
}
}

In this way, If I want to change the look of the loading indicator, I can just configure it in a single class.