Skip to content

Instantly share code, notes, and snippets.

@soapyigu
Created January 22, 2023 22:21
Show Gist options
  • Save soapyigu/311797657f59a795570b6806c4dc5c40 to your computer and use it in GitHub Desktop.
Save soapyigu/311797657f59a795570b6806c4dc5c40 to your computer and use it in GitHub Desktop.
Get an image from a url
// Convention: using GCD and URLSession
import Foundation
import UIKit
enum NetworkError: Error {
case invalidURL
case noData
case failedRequest
case returnedError
}
protocol NetworkManagable {
func get(_ url: String?, _ completion: @escaping (Result<Data, NetworkError>) -> Void)
}
class NetworkManager: NetworkManagable {
private let session = URLSession.shared
func get(_ url: String?, _ completion: @escaping (Result<Data, NetworkError>) -> Void) {
guard let urlStr = url, let realURL = URL(string: urlStr) else {
completion(.failure(.invalidURL))
return
}
let dataTask = session.dataTask(with: realURL) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
completion(.failure(.failedRequest))
return
}
guard error == nil else {
completion(.failure(.returnedError))
return
}
guard let data = data else {
completion(.failure(.noData))
return
}
completion(.success(data))
}
dataTask.resume()
}
}
enum ServiceError: Error {
case networkError(NetworkError)
case decodeError
}
class ImageService {
private var networkManager: NetworkManagable
init(networkManager: NetworkManagable = NetworkManager()) {
self.networkManager = networkManager
}
func getImage(_ url: String?, _ completion: @escaping (Result<UIImage, ServiceError>) -> Void) {
networkManager.get(url) { result in
switch result {
case .success(let data):
if let uiImage = UIImage(data: data) {
completion(.success(uiImage))
} else {
completion(.failure(.decodeError))
}
case .failure(let networkError):
completion(.failure(.networkError(networkError)))
}
}
}
}
// SwiftUI
// init(url:scale:)
AsyncImage(url: URL(string: urlStr))
.frame(width: 200, height: 200)
// init(url:scale:content:placeholder:)
AsyncImage(url: URL(string: "https://example.com/icon.png")) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50)
// init(url:scale:transaction:content:)
AsyncImage(url: URL(string: "https://example.com/icon.png")) { phase in
if let image = phase.image {
image // Displays the loaded image.
} else if phase.error != nil {
Color.red // Indicates an error.
} else {
Color.blue // Acts as a placeholder.
}
}
// Use the latest structured concurrency
import Foundation
import UIKit
enum NetworkError: Error {
case invalidURL
case noData
case invalidServerResponse
}
protocol NetworkManagable {
func get(_ url: String?) async throws -> Data
}
class NetworkManager: NetworkManagable {
private let session = URLSession.shared
func get(_ url: String?) async throws -> Data {
guard let urlStr = url, let realURL = URL(string: urlStr) else {
throw NetworkError.invalidURL
}
let (data, response) = try await session.data(for: URLRequest(url: realURL))
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw NetworkError.invalidServerResponse
}
guard !data.isEmpty else {
throw NetworkError.noData
}
return data
}
}
enum ImageServiceError: Error {
case unsupportedImage
}
class ImageService {
private var networkManager: NetworkManagable
init(networkManager: NetworkManagable = NetworkManager()) {
self.networkManager = networkManager
}
func getImage(for url: String?) async throws -> UIImage {
let data = try await networkManager.get(url)
if let image = UIImage(data: data) {
return image
} else {
throw ImageServiceError.unsupportedImage
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment