Saturday, June 01, 2019

100 Days of Swift

3. For a tougher challenge, take the image generation code out of cellForRowAt: generate all images when the app first launches, and use those smaller versions instead. For bonus points, combine the getDocumentsDirectory() method I introduced in project 10 so that you save the resulting cache to make sure it never happens again.

As a reminder, here’s the code for getDocumentsDirectory():
func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

The above is from Day 98 of Paul Hudson's excellent course: 100 Days of Swift. It is Challenge #3 and there is even a "bonus" challenge inside the challenge! In approaching the solution--before coding--I wrote down my thoughts and then gathered snippets of code that I thought would help. My notes are below. I thought it might be helpful for others to share them without giving away the solution.

First, I jotted down the four main things I was concerned with:
  1. Read big images from bundle, see project 1.
  2. Render a image into a rect using CoreGraphics like in project 30. 
  3. Read the thumbnails in like project 10
  4. If a thumbnail does not exist then create it. Also like in project 10.
Next, I gathered the corresponding snippets of the code:

From project 1:
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
    let fm = FileManager.default
    let path = Bundle.main.resourcePath!
    let items = try! fm.contentsOfDirectory(atPath: path)
    
    let pictureNames = items
        .filter { $0.hasPrefix("nssl") }
        .sorted { $0 < $1 }
    
    self?.storms = pictureNames.map {
        Storm(name: $0, imageName: $0) }
    
    DispatchQueue.main.async { [weak self] in
        self?.collectionView.reloadData()
    }
}

From project 30:
        let path = Bundle.main.path(forResource: imageRootName, ofType: nil)!
        let original = UIImage(contentsOfFile: path)!

        let renderRect = CGRect(origin: .zero, size: CGSize(width: 90, height: 90))
        let renderer = UIGraphicsImageRenderer(size: renderRect.size)
        
        let rounded = renderer.image { ctx in
            ctx.cgContext.addEllipse(in: renderRect)
            ctx.cgContext.clip()
            
            original.draw(in: renderRect)
        }

From project 10:

Write:
        let imageName = UUID().uuidString
        let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
        if let jpegData = image.jpegData(compressionQuality: 0.8) {
            try? jpegData.write(to: imagePath)
        }

Read:
        let path = getDocumentsDirectory().appendingPathComponent(person.imageName)
        cell.imageView.image = UIImage(contentsOfFile: path.path)