Home > Mobile >  Swift - Return Generic Type/Data
Swift - Return Generic Type/Data

Time:07-28

I have URLSession shared instance that fetches data. It returns a generic type. Everything works fine when I'm fetching data from a server and displaying it. But when I fetch an image I need to return the Generic Type as the data. How can I return the generic type/data so that it can be used to create the image in the imageView?

UIImageView:

extension UIImageView {
    
    func loadImage(with url: URL) {
        
        if let cachedImage = imageCache.object(forKey: url.absoluteString as AnyObject) as? UIImage {
            self.image = cachedImage
            return
        }
        
        URLSessionManager.fetchData(with: url, isImageFetch: true) { (result: Result<Data, SessionDataTaskError>) in

            switch result {

            case .failure(let error):

                print(error)

            case .success(let data):

                guard let downloadedImage = UIImage(data: data) else {
                    print("image data is nil")
                    return
                }

                DispatchQueue.main.async{ [weak self] in

                    imageCache.setObject(downloadedImage, forKey: url.absoluteString as AnyObject)
                    self?.image = downloadedImage
                }
            }
        }
    }
}

URLSessionManager:

class URLSessionManager {
    
    static let shared = URLSessionManager()
    
    static func fetchData<T: Decodable>(with url: URL, isImageFetch: Bool = false, completion: @escaping (Result<T, SessionDataTaskError>)->Void) { // SessionDataTaskError is just an Enum
        
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            
            if let error = error {
                completion(.failure(SessionDataTaskError.failedIssue(error)))
                return
            }
            
            guard let data = data else {
                completion(.failure(SessionDataTaskError.dataIsNil))
                return
            }
            
            if isImageFetch {

                // *** How can I return the generic type/data so that it can be used to create the image in the imageView? ***
                return
            }

            do {
                
                let genericType = try JSONDecoder().decode(T.self, from: data)
                
                completion(.success(genericType))
                
            } catch {
                
                completion(.failure(SessionDataTaskError.catchIssue(error)))
                
                guard let err = error as? DecodingError else {
                    debugPrint("decodingError-:", error)
                    return
                }
                
                switch err {
                case .typeMismatch(let key, let value):
                    print("decodingError-Mismatch: \(key), value \(value) and ERROR: \(error.localizedDescription)")
                case .valueNotFound(let key, let value):
                    print("decodingError-ValueNotFound: \(key), value \(value) and ERROR: \(error.localizedDescription)")
                case .keyNotFound(let key, let value):
                    print("decodingError-KeyNotFound(: \(key), value \(value) and ERROR: \(error.localizedDescription)")
                case .dataCorrupted(let key):
                    print("decodingError-DataCorrupted: \(key), and ERROR: \(error.localizedDescription)")
                default:
                    print("decodingError-UnknownError: \(error.localizedDescription)")
                }
            }
            
        }.resume()
    }
}

CodePudding user response:

The fetchData<T: Decodable> is for performing JSONDecoder on the body of the response. But an image is not JSON, so it does not make sense to call that method when the response is actually an image. Generally, you would have a separate implementation that returns a Data or a UIImage.

So I would have a non-generic fetchData that retrieves a Data upon success. Then I would have a generic fetchObject that calls fetchData decodes the generic with JSONDecoder. And I would have a non-generic fetchImage that also calls fetchData, but which returns a UIImage.


As an aside, I would be very careful with loadImage(with:). You do not just want to just update the UIImageView, because there is a risk (especially with collection and table views) where cells might be reused in the intervening time, which can cause very strange results where the wrong image may appear if you are scrolling quickly through the collection/table view). See https://stackoverflow.com/a/45183939/1271826 for an example of the use of “associated objects” to keep track of pending request, if any. This pattern also avoids it from getting backlogged if the user scrolls quickly.

  • Related