iOS API Service [Swift|Alamofire]

Nipun Ruwanpathirana
3 min readApr 1, 2020

In this short tutorial, you will learn how to write a thread safe API Service using below:

  1. Alamofire
  2. Codable

If you want to learn how to write a thread safe API Service in Android, Follow this

Add dependencies

CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'Alamofire', '~> 5.0'

Run pod install

Request and Response Classes

You can write Codable that easy to convert JSON.

  • Model Class
struct User: Codable {
var id:Int
var name: String?
var email: String?
}
//Your JSON will look like
{name: "Jhon", email: "jhon@example.com"}
  • Request Class
struct UserRequest: Codable(
var deviceId: String?
var user: User
)
//Your JSON will look like
{deviceId: "XXXX" user: {name: "Jhon", email: "jhon@example.com"}}
  • Response Class
struct BaseResponse: Codable {
var message: String = ""
var success: Boolean = false
}
struct UserResponse: Codable {
var message: String = ""
var success: Boolean = false
var user: User? = null
var token: String? = null
}
//Your JSON should look like
{token: "XXXX" user: {name: "Jhon", email: "jhon@example.com"}}

Service and Interface

  • API Service
import Foundation
import Alamofire
enum APIService: APIConfiguration { case getUser()
case createUser(request:UserRequest)
case updateUser(request:UserRequest)
// MARK: - HTTPMethod
var
method: HTTPMethod {
switch self {
case .getUser:
return .get
case .createUser:
return .post
case .updateUser:
return .put
}
}
// MARK: - Path
var
path: String {
switch self {
case .getUser:
return "v1/profile"
case .createUser:
return "v1/profile"
case .updateUser:
return "v1/profile"
}
}
// MARK: - Parameters
var
parameters: Parameters? {
switch self {
case .getUser:
return []
case .createUser(let request):
return request.request
case .updateUser(let request):
return request.request
}
}
// MARK: - URLRequestConvertible
func
asURLRequest() throws -> URLRequest {
var url = try ServerParam.baseURL!.asURL()
var urlRequest = URLRequest(url: url.appendingPathComponent(path))

// HTTP Method
urlRequest.httpMethod = method.rawValue
// Common Headers
urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.acceptType.rawValue)
urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.contentType.rawValue)
if let token = UserDefaultManager.getStringValue(key: UserDefaultManager.keyAPIToken) {
urlRequest.setValue(token, forHTTPHeaderField: HTTPHeaderField.authentication.rawValue)
}
// Parameters
if let parameters = parameters {
do {
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
}
return urlRequest
}
}
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
func toJSONData() -> Data? {
return try? JSONEncoder().encode(self)
}
}
  • API Client
import Foundation
import Alamofire
class APIServiceClient { static func getUser(completion:@escaping (Result<UserResponse, Error>) -> Void) {
AF.request(APIService.getUser())
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<UserResponse>) in
switch
response.result {
case .success:
completion(response.result)
case .failure(let error):
APIErrorManager.handleError(statusCode: response.response?.statusCode ?? (error as? URLError)?.errorCode ?? 0)
}
}
static func createUser(request: UserRequest, completion:@escaping (Result<UserResponse, Error>) -> Void) {
AF.request(APIService.createUser(request:request))
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<BaseResponse>) in
switch
response.result {
case .success:
completion(response.result)
case .failure(let error):
APIErrorManager.handleError(statusCode: response.response?.statusCode ?? (error as? URLError)?.errorCode ?? 0)
}
}
static func updateUser(request: UserRequest, completion:@escaping (Result<UserResponse, Error>) -> Void) {
AF.request(APIService.updateUser(request:request))
.validate(statusCode: 200..<300)
.responseDecodable { (response: DataResponse<BaseResponse>) in
switch
response.result {
case .success:
completion(response.result)
case .failure(let error):
APIErrorManager.handleError(statusCode: response.response?.statusCode ?? (error as? URLError)?.errorCode ?? 0)
}
}
}
  • Error Handler
class APIErrorManager {
static func handleError(statusCode: Int) {
switch statusCode {
case 401:
//Invalid Session
case -1009, -1005:
//No internet
case -1000, -1001, -1002, -1003:
//Can't Connect to server
default:
//Log Error
}
}
}

In your ViewController

APIServiceClient.getUser() { result in
switch result {
case .success:
//200
case .failure:
//Error
}
}

From this post, we’ve learned how to write a thread safe API Service in iOS.

If you want to learn how to thread safe API Service Android, Follow this

Thank you for reading.

--

--