Day 23 - 100 Days of Swift

4 minute read

Consolidation II (Projects 1-3)

Day 23 is a consolidation day where you review the first three projects. You go back over a bunch of stuff covered in the few days, but I won’t reiterate it here. He also gives you a challenge to build an app that displays all the flags from the flag game on a table view, lets you view them in a detail view, and lets you share the image and country name. It is very similar to the other projects, so I’ll move pretty quickly.

First, I added a countries variable to hold an array of country names and filled it up in viewDidLoad :

1
2
3
4
5
6
7
8
9
10
11
12
13
var countries: [String] = []

override func viewDidLoad() {
    super.viewDidLoad()

    let fm = FileManager.default
    let path = Bundle.main.resourcePath!
    let items = try! fm.contentsOfDirectory(atPath: path)

    countries = items.filter({ $0.hasSuffix("png")})
        .map({ $0.components(separatedBy: ".")[0] })

}

I filtered to only the items with a png suffix and then chopped off everything from the . and after with a chain of higher order functions. This leaves an array with all the country names as they appear in the image names.

Next, I edited ViewController to inherit from UITableViewController, added a table view controller as the main view in the storyboard, and set its class to be ViewController. This allowed me to override the UITableViewControllerDataSource methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
override func tableView(_ tableView: UITableView,
                        numberOfRowsInSection section: Int) -> Int {
    return countries.count
}

override func tableView(_ tableView: UITableView,
                        cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "FlagCell",
                                             for: indexPath)
    let country = countries[indexPath.row]

    // Uppercase abbreviated names and capitalize full names
    cell.textLabel?.text = country.count < 3 ?
        country.uppercased() :
        country.capitalized
    cell.imageView?.image = UIImage(named: country)

    return cell
}

Next, I added a new view controller to the storyboard, gave it an image view, and constrained to be big enough to display a flag:

Screenshot of adding image view to view controller on storyboard.

Then I made a new DetailViewController class that inherits from UIViewController , set the new view controller’s class to it, and connected an outlet from the image view. Then I added a setupViews() method to set the views up:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private func setupViews() {
    guard let countryName = countryName else { return }
    navigationItem.rightBarButtonItem =
        UIBarButtonItem(barButtonSystemItem: .action,
                        target: self,
                        action: #selector(shareFlag))

    title = countryName.count < 3 ?
        countryName.uppercased() :
        countryName.capitalized

    flagImageView.image = UIImage(named: countryName)
    flagImageView.addBorder()
}

You can see that the image view uses an addBorder() method, which is similar to the one I made the other day. I just configured it to round the corners a little bit as well:

1
2
3
4
5
6
7
8
9
10
extension UIView {
    func addBorder(width: CGFloat = 1, color: UIColor = .gray) {
        self.layer.borderWidth = width
        self.layer.borderColor = color.cgColor

        self.layer.cornerRadius =
            min(self.frame.height, self.frame.width) / 24
        self.layer.masksToBounds = true
    }
}

You can also see that the bar button item’s action is a method called shareFlag so the last thing I did was add that and give a Photo Library Addition Usage to Info.plist:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@objc private func shareFlag(action: UIAlertAction! = nil) {
    guard let imageName = countryName,
        let image = UIImage(named: imageName),
        let imageData = image.jpegData(compressionQuality: 0.8) else {
            return
    }

    let items: [Any] = [imageData, imageName]
    let activityVC =
        UIActivityViewController(activityItems: items,
                                 applicationActivities: [])

    activityVC.popoverPresentationController?.barButtonItem =
        navigationItem.rightBarButtonItem

    present(activityVC, animated: true)

}

At the end of the day, it looks like this:

Screenshots of working app.

You can find my version of this project at the end of day 23 on Github here.

Reflections

Today was definitely a good review. Most of these things are things I’m familiar with, and have done many times. But I did have to look up how to get the filenames from from the main bundle. So I can’t say I knew it all by heart.

Slowly but surely, these project apps are getting more complicated and more interesting. I’m excited to see where things go next!