Автоматическая загрузка картинки по сети

swift
ios

#1

Очень часто мы таскаем изображения по сетке и присваиваем их для UImageView. При этом приходится писать кучу лишнего кода для получения даты для картинки, преобразование ее в саму картинку, передавать управление потока в основной и так далее…

Все это можно решить достаточно просто, если помнить про расширения базовых классов!

К примеру, вот такой кусочек кода сильно облегчает работу с приведенным примером:

 extension UIImageView {
   
   func downloadedFrom(link:String) {
    guard let url = URL(string: link) else { return }
    URLSession.shared.dataTask(with: url, completionHandler: { (data, _, error) -> Void in
      guard let data = data , error == nil, let image = UIImage(data: data) else { return }
      DispatchQueue.main.async { () -> Void in
        self.image = image
      }
    }).resume()
  }
  
}

По традиции небольшой пример использования:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
    cell?.imageView?.downloadedFrom(link: "http://swiftbook.ru/sites/default/files/images/ivan.png")
    return cell!
  }

Ну и как результат, немного Ивана вам в ленту :flushed: :


#2

Как хорошая альтернатива самописному коду - AlamofireImage, с кучей свистелок и кешем. Из плюсов - делается все само и помогает думать о других важных и не очень вещах. Из минусов - можно легко забыть основы работы с сетью, потоками и прочим. ))


#3

Загрузка данных во вью плохая практика :slight_smile: И чтение из url кэша медленнее чем просто из файла (при прокрутки в TableView сильно заметно).

У меня этот процесс происходит куда интереснее:

import Foundation
import CoreData
import CommonCrypto

private let backgroundOperationQueue = OperationQueue()
private var urlSession: URLSession = {
    let configuration = URLSessionConfiguration.default
    configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
    return URLSession(configuration: configuration, delegate: nil, delegateQueue: backgroundOperationQueue)
}()

extension Image {
    
    var background: Image {
        return CDContext.background.object(with: objectID) as! Image
    }
    
    convenience init(_ url: String) {
        let context = CDContext.background
        let description = NSEntityDescription.entity(forEntityName: "Image", in: context)!
        self.init(entity: description, insertInto: context)
        self.url = url
    }
    
    func getData(completion: @escaping (Data, Bool) -> ()) {
        
        let mainCompletion = { (data: Data, animated: Bool) -> () in
            DispatchQueue.main.async { completion(data, animated) }
        }
        
        if let path = path {
            backgroundOperationQueue.addOperation {
                let data = NSData(contentsOfFile: path) as? Data ?? Data()
                mainCompletion(data, false)
            }
        } else {
            urlSession.dataTask(with: URL(string: url!)!) { data, _, _ in
                let imagePath = imagesCachePath + "/" + self.url!.md5
                if let data = data {
                    mainCompletion(data, true)
                    if NSData(data: data).write(toFile: imagePath, atomically: true) {
                        let context = CDContext.background
                        context.perform {
                            let image = self.background
                            image.path = imagePath
                            try? context.save()
                        }
                    }
                }
            }.resume()
        }
    }
}

var imagesCachePath: String! = {
    guard let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else { return nil }
    let imagePath = path + "/Images"
    let fileManager = FileManager.default
    if !fileManager.fileExists(atPath: imagePath) {
        do {
            try fileManager.createDirectory(atPath: imagePath, withIntermediateDirectories: true)
        } catch {
            print(error)
            return nil
        }
    }
    return imagePath
}()


private extension String {
    var md5: String {
        var bytes = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(self, CC_LONG(utf8.count), &bytes)
        return bytes.map { String(format: "%02x", $0) }.joined()
    }
}

#4

Твоя правда! Но есть проекты, где не стоит сильно заморачиваться. А просто написать небольшой участок и все будет работать. Ну а табличка просто для примера, чтоб не думали где можно применить. По хорошему счету на ячейку тогда метод конфигурации оной писать следует )))