Created
August 15, 2019 09:03
-
-
Save tobitech/1c18017d7d3ee000597aedb70e96c7c6 to your computer and use it in GitHub Desktop.
APIService examples using Swift 5 Result Type
This file contains hidden or 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
// model for user profile | |
struct UserProfile: Codable { | |
let email: String? | |
let names: String? | |
let phone: String? | |
} | |
// model for all api response with generic data | |
struct DataResponse<T: Decodable>: Decodable { | |
let status: Int? | |
let message: String? | |
let data: T? | |
} | |
// model for all api response without data | |
struct BasicResponse: Decodable { | |
let status: Int? | |
let message: String? | |
} | |
// api service class that handles requests | |
class APIService { | |
func signupUser(phone: String, email: String, password: String, completion: @escaping(Result<UserProfile?, Error>) -> Void) { | |
let body = [ | |
"phone": "\(phone)", | |
"email": "\(email)", | |
"password": "\(password)" | |
] | |
// you don't have to do this: this just abstract the creation of requests... | |
// you can create URLSession data task request as you would normally do | |
let dataLoader = DataLoader() | |
dataLoader.request(endpoint: Endpoint.signup(), requestMethod: .post, bodyParameters: body) { (response: DataResponse<UserProfile>?, error) in | |
if let err = error { | |
completion(.failure(err)) | |
return | |
} | |
// success | |
guard let status = response?.status?.boolValue else { return } | |
if status { | |
completion(.success((response?.data))) | |
} | |
} | |
} | |
} | |
// Sample usage | |
class SignupLogicController { | |
typealias Handler = (UserProfileState) -> Void | |
func signup(with phone: String, email: String, password: String, then handler: @escaping Handler) { | |
let authService = AuthService() | |
authService.signupUser(phone: phone, email: email, password: password) { result in | |
switch result { | |
case .success(let user): | |
handler(.succeeded(user)) | |
case .failure(let error): | |
handler(.failed(error)) | |
} | |
} | |
} | |
} |
This file contains hidden or 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
// you can use this... | |
class DataLoader { | |
// MARK: - Properties | |
var defaultSession: URLSession! | |
var dataTask: URLSessionDataTask? | |
var urlRequest: URLRequest! | |
lazy var headers = [ | |
"Content-Type": "application/json" | |
] | |
init(_ urlSession: URLSession = URLSession(configuration: URLSessionConfiguration.default)) { | |
self.defaultSession = urlSession | |
} | |
func request<T: Decodable>(endpoint: Endpoint, requestMethod: HTTPMethod, bodyParameters: [String: Any]?, completion: @escaping (_ response: DataResponse<T>?, _ error: Error?) ->()) { | |
// setup the request | |
guard let url = endpoint.url else { return } | |
urlRequest = URLRequest(url: url) | |
urlRequest.allHTTPHeaderFields = [ | |
"Content-Type": "application/json" | |
] | |
urlRequest.httpMethod = requestMethod.rawValue | |
if let bParams = bodyParameters { | |
do { | |
let postData = try JSONSerialization.data(withJSONObject: bParams, options: []) | |
urlRequest.httpBody = postData as Data | |
} catch let serializeErr { | |
print("failed to serialize params: ", serializeErr) | |
} | |
} | |
dataTask = defaultSession.dataTask(with: urlRequest, completionHandler: { (data, response, error) in | |
defer { self.dataTask = nil } | |
if let error = error { | |
print("DataTask error: " + error.localizedDescription) | |
completion(nil, error) | |
} else if let data = data { | |
if let response = response as? HTTPURLResponse, response.statusCode == 200 { | |
do { | |
let obj = try JSONDecoder().decode(DataResponse<T>.self, from: data) | |
completion(obj, nil) | |
} catch let decodeError { | |
print("-----------------Unencoded Error below--------------------------") | |
print("Request URL: \(url)") | |
print(decodeError.localizedDescription, decodeError) | |
let undecodable = self.deserializeUnencodable(data: data) | |
if let message = undecodable["message"] as? String { | |
print(message) | |
completion(nil, decodeError) | |
} else if let payload = undecodable["message"] as? [String: Any], let message = payload["message"] as? String { | |
print(message) | |
completion(nil, decodeError) | |
} else { | |
completion(nil, decodeError) | |
} | |
} | |
} else { | |
do { | |
let result = try JSONDecoder().decode(DataResponse<T>.self, from: data) | |
let _ = self.deserializeUnencodable(data: data) | |
completion(result, nil) | |
} catch { | |
completion(nil, error) | |
} | |
} | |
} else { | |
completion(nil, error) | |
} | |
}) | |
dataTask?.resume() | |
} | |
func request(endpoint: Endpoint, requestMethod: HTTPMethod, bodyParameters: [String: Any]?, completion: @escaping (_ response: BasicResponse?, _ error: Error?) ->()) { | |
// setup the request | |
guard let url = endpoint.url else { return } | |
urlRequest = URLRequest(url: url) | |
urlRequest.allHTTPHeaderFields = [ | |
"Content-Type": "application/json" | |
] | |
urlRequest.httpMethod = requestMethod.rawValue | |
if let bParams = bodyParameters { | |
do { | |
let postData = try JSONSerialization.data(withJSONObject: bParams, options: []) | |
urlRequest.httpBody = postData as Data | |
} catch let serializeErr { | |
print("failed to serialize params: ", serializeErr) | |
} | |
} | |
dataTask = defaultSession.dataTask(with: urlRequest, completionHandler: { (data, response, error) in | |
defer { self.dataTask = nil } | |
if let error = error { | |
print("DataTask error: " + error.localizedDescription) | |
completion(nil, error) | |
} else if let data = data { | |
if let response = response as? HTTPURLResponse, response.statusCode == 200 { | |
do { | |
let obj = try JSONDecoder().decode(BasicResponse.self, from: data) | |
completion(obj, nil) | |
} catch let decodeError { | |
print("-----------------Unencoded Error below--------------------------") | |
print("Request URL: \(url)") | |
print(decodeError.localizedDescription, decodeError) | |
let undecodable = self.deserializeUnencodable(data: data) | |
if let message = undecodable["message"] as? String { | |
print(message) | |
completion(nil, decodeError) | |
} else if let payload = undecodable["message"] as? [String: Any], let message = payload["message"] as? String { | |
print(message) | |
completion(nil, decodeError) | |
} else { | |
completion(nil, decodeError) | |
} | |
} | |
} else { | |
do { | |
let result = try JSONDecoder().decode(BasicResponse.self, from: data) | |
let _ = self.deserializeUnencodable(data: data) | |
completion(result, nil) | |
} catch { | |
completion(nil, error) | |
} | |
} | |
} else { | |
completion(nil, error) | |
} | |
}) | |
dataTask?.resume() | |
} | |
private func deserializeUnencodable(data: Data) -> [String: Any] { | |
do { | |
let result = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] | |
print("-----------------Unencodable below--------------------------") | |
print(result ?? [:] ) | |
return result ?? [:] | |
} catch let err { | |
print("failed to deserialize unencodable", err) | |
return [:] | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment