Skip to content

Instantly share code, notes, and snippets.

@tobitech
Created August 15, 2019 09:03
Show Gist options
  • Save tobitech/1c18017d7d3ee000597aedb70e96c7c6 to your computer and use it in GitHub Desktop.
Save tobitech/1c18017d7d3ee000597aedb70e96c7c6 to your computer and use it in GitHub Desktop.
APIService examples using Swift 5 Result Type
// 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))
}
}
}
}
// 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