Networking is a design-pattern framework based on Apple's URLSession. It's evolved from the idea of replacing Alamofire (in some projects also Moya) with native URLSession functions.
Dependency Graph draw.io
- iOS 10.0+
- Xcode 12+
- Swift 5.3+
Framework is supported only via Swift Package Manager and is not planned to be distributed otherwise, unless there is a specific requirement. As Swift Package Manager is the first official distribution tool managed by Apple for Swift, it's stable currently and there is an active development on it which promises it will be used for a while.
Project can be installed using SPM with its github URL.
https://github.com/mobven/NetworkingNetworkable functionalities can be achieved creating enums which conform to Networkable protocol and overriding its request parameter. Later, when fetch is called, Networkable uses this request to proceed the request.
import MBNetworking
enum API {
enum Login: Networkable {
/// Login with username and password with GET request.
case loginGet(username: String, password: String)
/// Login with username and password with POST request.
case loginPost(request: LoginRequest)
var request: URLRequest {
switch self {
case let .loginGet(username, password):
return getRequest(url: API.getURL(endpoint: "LoginPOST"),
queryItems: ["username": username, "password": password])
case let .loginPost(request):
return getRequest(url: API.getURL(endpoint: "LoginGET"),
encodable: request)
}
}
}
private static func getURL(endpoint: String) -> URL {
let baseEndpoint = "https://example.com/api/"
return URL(forceString: "\(baseEndpoint)\(endpoint)")
}
}
struct LoginRequest: Encodable {
var username: String
var password: String
}API.Login.loginGet(username: "admin", password: "admin").fetch(LoginResponse.self) { result in
switch result {
case let .success(response):
print("Succeeded with \(response)")
case let .failure(error):
print("Failed with \(error)")
}
}
API.Login.loginPost(request: LoginRequest(username: "admin", password: "admin")).fetch(LoginResponse.self) { result in
switch result {
case let .success(response):
print("Succeeded with \(response)")
case let .failure(error):
print("Failed with \(error)")
}
}
struct LoginResponse: Decodable {
var name: String?
var surname: String?
}By default, request timeouts are 60 seconds for request and resource which are relatively equivalent to URLSessionConfiguration's timeoutIntervalForRequest and timeoutIntervalForResource parameters. Currently Networking supports timeouts to be set globally, so once changed new value will be applied to all next requests. Having different timeout for each single request is planned to be supported in the future to enable Networkable include timeout parameter.
NetworkableConfigs.default.setTimeout(for: 30, resource: 30)Networking, like setting timeout, supports only global certificates set via:
if let path = Bundle.main.path(forResource: "certificate", ofType: "der") {
NetworkableConfigs.default.setCertificatePaths(path)
}Networking can be configured to continue with any challenge, in case your server has no publicly trusted certifiace.
Apple may reject your application, for this usage. It's on your own responsibility
NetworkableConfigs.default.setServerTrustedURLAuthenticationChallenge()Implement this protocol to handle tasks creation, data receiving, and task completion with error.
- Set monitoring delegate.
Networkingcan pass data, tasks and errors of URLSession by NetworkLogMonitoringDelegate methods.
NetworkableConfigs.default.set(networkLogMonitoringDelegate: NetworkLogMonitoringDelegate)- Implement the
NetworkLogMonitoringDelegateprotocol to your class.
class YourClass: NetworkLogMonitoringDelegate {
// Implement protocol methods
///Called when a URLSessionTask is created.
func logTaskCreated(task: URLSessionTask) { /* Handle task creation */ }
///Called when a URLSessionDataTask receives data.
func logDataTask(dataTask: URLSessionDataTask, didReceive data: Data) { /* Handle data receive */ }
//Called when a URLSessionTask completes with error.
func logTask(task: URLSessionTask, didCompleteWithError error: Error) { /* Handle task completion with error */ }
}Networking supports file upload through uploadRequest using MBNetworking.Files.
extension API {
enum File: Networkable {
/// Multipart file upload request.
case upload(parameters: [String: String], files: [MBNetworking.File])
var request: URLRequest {
switch self {
case let .upload(parameters, files):
return uploadRequest(url: API.getURL(endpoint: "FileUpload"),
parameters: parameters, files: files)
}
}
}
}You can use Data type as decodable to download Data from service. This can be helpful for downloading image, html data.
extension API {
enum Download: Networkable {
case image(url: URL)
var request: URLRequest {
switch self {
case let .image(url):
return getRequest(url: url, queryItems: [:])
}
}
}
}Networking provides StubURLProtocol which acts like man-in-the-middle to simplify stub data for unit testing. You can achieve this by setting StubURLProtocol.result to a any of the available values which are:
- success(Data): Successfull result with specified data
- failure(Error): Failure with the specified Error.
- case failureStatusCode(Int): Failure with the specified status code.
There're helper functions to get the StubURLProtocol.Result with the data from specified bundle resource.
StubURLProtocol.result = .getData(from: Bundle.module.url(forResource: "some", withExtension: "txt"))
StubURLProtocol.result = .getData(from: Bundle.module.path(forResource: "some", ofType: "txt"))You can define a delay in seconds for reading stub data:
StubURLProtocol.delay = 3StubURLProtocol is designed for unit tests only and would not work if there's no any unit testing process in progress.
You need to set StubURLProtocol.result before each Networkable.fetch call to achieve necessary result.
Networking supports cancelling ongoing requests. It can be helpful when you need to cancel all fired requests without waiting to respond them.
NetworkableTasks.cancelAll()After calling cancelAll() function, ongoing request returns NetworkingError.dataTaskCancelled in their failure block immediately.
Networking allows to set URLSessionConfiguration through its NetworkableConfigs. Possible values can be default and ephemeral.
By default, MBNetworking uses URLSessionConfiguration.default.
NetworkableConfigs.default.set(configuration: .ephemeral)Networking supports custom URLSessionDelegate as PinnableSessionDelegate.
- parameter challenge: PinnableSessionDelegate: The PinnableSessionDelegate protocol conforms to the URLSessionDelegate protocol.
public func setServerTrustedAuthenticationChallenge(_ challenge: PinnableSessionDelegate) - Async/await support with Swift 5.5
