Here’s an hour-long livecoding video we did last week to make a basic table view app that downloads and parses JSON and builds a set of dynamic, expanding table view cells. Lots of helfup tips and tricks for working with Xcode and Swift are sprinkled throughout. The resulting code is available on Github.
Want to be notified when livecoding is coming up? Follow @nickoneill on Twitter.
Some relevant code snippits from the video so you can follow along:
This is our viewDidLoad
override in our main view controller. Note the estimatedRowHeight
so that we can automatically grow and shrink our table view cell sizes.
Our ColorClient
(code below), fetches a list of colors and returns them to us as ColorBox
objects.
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 125
tableView.rowHeight = UITableViewAutomaticDimension
ColorClient.sharedClient.getColors {[weak self](colors) in
self?.colors = colors
dispatch_async(dispatch_get_main_queue(), {
self?.tableView.reloadData()
if colors.count > 0 {
self?.selected(colors.first!.color)
}
})
}
}
The ColorBox
object uses a failable initializer based on if we can correctly decode the correct data from the JSON file. Note the guard
usage here!
struct ColorBox {
let name: String
let desc: String
let color: UIColor
init?(json: Dictionary<String, AnyObject>) {
guard let name = json["name"] as? String else {
return nil
}
self.name = name
guard let colors = json["rgb"] as? [Int] where colors.count == 3 else {
return nil
}
let color = UIColor(red: CGFloat(colors[0]) / 255, green: CGFloat(colors[1]) / 255, blue: CGFloat(colors[2]) / 255, alpha: 1)
self.color = color
if let desc = json["description"] as? String {
self.desc = desc
} else {
self.desc = ""
}
}
}
Configuring our table cell display is simple and important. I always move this into a configure
method on my custom table view cells.
class ColorBoxTableViewCell: UITableViewCell {
@IBOutlet weak var colorView: UIView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var descLabel: UILabel!
func configure(color: ColorBox) {
titleLabel.text = color.name
descLabel.text = color.desc
colorView.backgroundColor = color.color
}
}
The core of the app is a custom API client, based on the Swift API client we featured here previously.
class ColorClient {
static let sharedClient = ColorClient()
func getColors(completion: ([ColorBox]) -> ()) {
get(clientURLRequest("videosrc/colors.json")) { (success, object) in
var colors: [ColorBox] = []
if let object = object as? Dictionary<String, AnyObject> {
if let results = object["results"] as? [Dictionary<String, AnyObject>] {
for result in results {
if let color = ColorBox(json: result) {
colors.append(color)
}
}
}
}
completion(colors)
}
}
private func get(request: NSMutableURLRequest, completion: (success: Bool, object: AnyObject?) -> ()) {
dataTask(request, method: "GET", completion: completion)
}
private func clientURLRequest(path: String, params: Dictionary<String, AnyObject>? = nil) -> NSMutableURLRequest {
let request = NSMutableURLRequest(URL: NSURL(string: "https://thatthinginswift.com/"+path)!)
return request
}
private func dataTask(request: NSMutableURLRequest, method: String, completion: (success: Bool, object: AnyObject?) -> ()) {
request.HTTPMethod = method
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if let data = data {
let json = try? NSJSONSerialization.JSONObjectWithData(data, options: [])
if let response = response as? NSHTTPURLResponse where 200...299 ~= response.statusCode {
completion(success: true, object: json)
} else {
completion(success: false, object: json)
}
}
}.resume()
}
}