I've recently worked on a reusable network service class for a service-aggregator iOS app. This class should retry a failed request if it was caused by expired user token. Plus, this class will be used between multiple contractors, who will create the aggregated services.
Since the contractors might use different authentication methods, I create a interface / protocol for the class that will manage user's authentication. Here's a sample of my work (in Swift):
BaseNetworkService.swift
class BaseNetworkService {
/**
Request headers that will be used by this instance.
*/
internal var requestHeaders = ["Content-Type": "application/json"]
/**
Request GET method to `URL` with passed `parameters`. Will send success response
in `next` and failure in `error` event of returned `RACSignal` instance.
*/
internal func GET(URL: String, parameters: [String: AnyObject]) -> RACSignal {
// method implementation here
}
// Rest of HTTP methods here
}
AuthenticatedNetworkService.swift
class AuthenticatedNetworkService: BaseNetworkService {
/**
Used to retrieve authentication-related request headers
and refresh expired user token.
*/
private var authService: AuthenticationProtocol
init(authService: AuthenticationProtocol) {
self.authService = authService
}
/**
Request GET method to `URL` with passed `parameters`. Will send success response
in `next` and failure in `error` event of returned `RACSignal` instance.
- note: If the request fails because of expired user token, this instance will
refresh current user token, and retry it once again.
*/
override func GET(URL: String, parameters: [String: AnyObject]) -> RACSignal {
// method implementation here
}
// Rest of HTTP methods here
}
AuthenticationProtocol.swift
protocol AuthenticationProtocol {
/**
Stores authentication header name in the `key`, and its value in its
corresponding `value`.
*/
internal var authenticationHeaders: [String: String] { get }
/**
Checks whether passed `error` caused by expired user token or not.
*/
func isErrorCausedByExpiredToken(error: NSError) -> Bool
/**
Refresh this instance's `authenticationHeaders`. It send `next:` event from
returned `RACSignal` if the process succeeds, and `error:` otherwise.
*/
func refreshAuthenticatioHeaders() -> RACSignal
}
I was reading about Design Patterns while working on this, and created a Factory-like class for creating the AuthenticatedNetworkService for the aggregated services. Something around this:
class AuthenticatedNetworkServiceFactory {
/**
Returns network service for Pizza Delivery service.
*/
class func PizzaDeliveryNetworkService() -> AuthenticatedNetworkService {
let authService = PizzaDeliveryAuthenticationService()
return AuthenticatedNetworkService(authService: authService)
}
/**
Returns network service for Quick Laundry service.
*/
class func QuickLaundryNetworkService() -> AuthenticatedNetworkService {
let authService = QuickLaundryAuthenticationService()
return AuthenticatedNetworkService(authService: authService)
}
/**
Returns network service for Cab Finder service.
*/
class func CabFinderNetworkService() -> AuthenticatedNetworkService {
let authService = CabFinderAuthenticationService()
return AuthenticatedNetworkService(authService: authService)
}
}
Yet, after revisiting the GoF book, I found that Factory pattern was meant to return subclasses instead of a the main class. Since I was returning the main class (AuthenticatedNetworkService
), is the AuthenticatedNetworkServiceFactory
could be considered as a Factory? Or was it just a Helper class?