Last active
July 10, 2024 20:48
-
-
Save aryamansharda/be72b3abfe86c9e424c4df6fc1c5615c to your computer and use it in GitHub Desktop.
Network Layer Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum HTTPMethod: String { | |
case delete = "DELETE" | |
case get = "GET" | |
case patch = "PATCH" | |
case post = "POST" | |
case put = "PUT" | |
} | |
enum HTTPScheme: String { | |
case http | |
case https | |
} | |
/// The API protocol allows us to separate the task of constructing a URL, | |
/// its parameters, and HTTP method from the act of executing the URL request | |
/// and parsing the response. | |
protocol API { | |
/// .http or .https | |
var scheme: HTTPScheme { get } | |
// Example: "maps.googleapis.com" | |
var baseURL: String { get } | |
// "/maps/api/place/nearbysearch/" | |
var path: String { get } | |
// [URLQueryItem(name: "api_key", value: API_KEY)] | |
var parameters: [URLQueryItem] { get } | |
// "GET" | |
var method: HTTPMethod { get } | |
} | |
// This particular API will not work without an API KEY | |
enum GooglePlacesAPI: API { | |
case getNearbyPlaces(searchText: String?, latitude: Double, | |
longitude: Double) | |
var scheme: HTTPScheme { | |
switch self { | |
case .getNearbyPlaces: | |
return .https | |
} | |
} | |
var baseURL: String { | |
switch self { | |
case .getNearbyPlaces: | |
return "maps.googleapis.com" | |
} | |
} | |
var path: String { | |
switch self { | |
case .getNearbyPlaces: | |
return "/maps/api/place/nearbysearch/json" | |
} | |
} | |
var parameters: [URLQueryItem] { | |
switch self { | |
case .getNearbyPlaces(let query, let latitude, let longitude): | |
var params = [ | |
URLQueryItem(name: "key", value: GooglePlacesAPI.key), | |
URLQueryItem(name: "language", | |
value: Locale.current.languageCode), | |
URLQueryItem(name: "type", value: "restaurant"), | |
URLQueryItem(name: "radius", value: "6500"), | |
URLQueryItem(name: "location", | |
value: "\(latitude),\(longitude)") | |
] | |
if let query = query { | |
params.append(URLQueryItem(name: "keyword", value: query)) | |
} | |
return params | |
} | |
} | |
var method: HTTPMethod { | |
switch self { | |
case .getNearbyPlaces: | |
return .get | |
} | |
} | |
} | |
// Network Layer Implementation | |
final class NetworkManager { | |
/// Builds the relevant URL components from the values specified | |
/// in the API. | |
private class func buildURL(endpoint: API) -> URLComponents { | |
var components = URLComponents() | |
components.scheme = endpoint.scheme.rawValue | |
components.host = endpoint.baseURL | |
components.path = endpoint.path | |
components.queryItems = endpoint.parameters | |
return components | |
} | |
/// Executes the HTTP request and will attempt to decode the JSON | |
/// response into a Codable object. | |
/// - Parameters: | |
/// - endpoint: the endpoint to make the HTTP request to | |
/// - completion: the JSON response converted to the provided Codable | |
/// object when successful or a failure otherwise | |
class func request<T: Decodable>(endpoint: API, completion: @escaping (Result<T, Error>) -> Void) { | |
let components = buildURL(endpoint: endpoint) | |
guard let url = components.url else { | |
Log.error("URL creation error") | |
return | |
} | |
var urlRequest = URLRequest(url: url) | |
urlRequest.httpMethod = endpoint.method.rawValue | |
let session = URLSession(configuration: .default) | |
let dataTask = session.dataTask(with: urlRequest) { data, response, error in | |
if let error = error { | |
completion(.failure(error)) | |
Log.error("Unknown error", error) | |
return | |
} | |
guard response != nil, let data = data else { | |
return | |
} | |
if let responseObject = try? JSONDecoder().decode(T.self, from: data) { | |
completion(.success(responseObject)) | |
} else { | |
let error = NSError(domain: "com.AryamanSharda", code: 200, userInfo: [NSLocalizedDescriptionKey: "Failed"]) | |
completion(.failure(error)) | |
} | |
} | |
dataTask.resume() | |
} | |
} | |
// UIImageView extension to load images from a URL | |
import UIKit | |
let imageCache = NSCache<NSString, UIImage>() | |
extension UIImageView { | |
/// Loads an image from a URL and saves it into an image cache, returns | |
/// the image if already available in the cache. | |
/// - Parameter urlString: String representation of the URL to load the | |
/// image from | |
/// - Parameter placeholder: An optional placeholder to show while the | |
/// image is being fetched | |
/// - Returns: A reference to the data task in order to pause, cancel, | |
/// resume, etc. | |
@discardableResult | |
func loadImageFromURL(urlString: String, placeholder: UIImage? = nil) -> URLSessionDataTask? { | |
self.image = nil | |
let key = NSString(string: urlString) | |
if let cachedImage = imageCache.object(forKey: key) { | |
self.image = cachedImage | |
return nil | |
} | |
guard let url = URL(string: urlString) else { | |
return nil | |
} | |
if let placeholder = placeholder { | |
self.image = placeholder | |
} | |
let task = URLSession.shared.dataTask(with: url) { data, _, _ in | |
DispatchQueue.main.async { | |
if let data = data, let downloadedImage = UIImage(data: data) { | |
imageCache.setObject(downloadedImage, forKey: NSString(string: urlString)) | |
self.image = downloadedImage | |
} | |
} | |
} | |
task.resume() | |
return task | |
} | |
} | |
// Usage: | |
let endpoint = GooglePlacesAPI.getNearbyPlaces(searchText: query, latitude: latitude, longitude: longitude) | |
NetworkManager.request(endpoint: endpoint) { [weak self] | |
(result: Result<NearbyPlacesResponse, Error>) in | |
switch result { | |
case .success(let response): | |
self?.dataSource = response.results | |
self?.tableView.reloadData() | |
case .failure(let error): | |
Log.error(error) | |
} | |
} | |
How to use this with third party library Alamofire and Moya ,
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hey bro thanks so much