NerdzNetworking is a wrapper on top of URLSession and URLRequest to simplify creating and managing network requests written on Swift language.
Example
You need to define request.
class LoginWithFacebookRequest: Request {
typealias ResponseObjectType = User
typealias ErrorType = AuthError
let path = "login/facebook"
let methong = .post
let body: RequestBody?
init(token: String) {
body = .params(
[
"token": token
]
)
}
}
And then just use it. (each call is optional and can be skipped if needed)
LoginWithFacebookRequest(token: fbToken)
.execute()
.onSuccess { user in
...
}
.onFail { error in
...
}
.onStart { operation in
...
}
.onProgress { progress in
...
}
.onDebug { info in
...
}
}
Ideology
Structure
The main ideology for creating NerdzNetworking library was to maximaly split networking into small pieces.
The ideal scenario would be to have a separate class/structure per each request. This should help easily navigate and search information for specific request in files structure.
Strong typization
Another flow that library truing to follow - predefined options for using and simplicity of using.
We are trying to use generic types on top of protocols, as well as enumerations instead of raw values. As an example - predefined headers like Content-Type or Accept. Instead of giving a possibility to put any value, we have defined and enumerations that limits possbie input only to predefined scenarios like .application(.json).
To make it simple, previously mentioned headers already have .application(.json) value preselected for you, so in case you are using standard REST API - everything ready from the box.
Tutorial
Endpoint setup
First of all you need to setup your endpoint that will be used later on for executing requests. To do that - you should be using Endpoint class.
Endpoint class will collect all general settings for performing requests. You can change any parameter at any time you want.
let endpoint = Endpoint(baseUrl: myBaseUrl)
endpoint.headers = defaultHeaders // Specifying some default headers like OS, device language, device model, etc.
endpoint.headers.authToken = .bearer(tokenString) // Specifying user token
After creating your endpoint, you can mark it as a default, so every request will pick it up automatically.
Endpoint.default = endpoint
You can change default endpoint based on configuration or environment you need.
Request creation
To create a request you should implement Request protocol. You can or have separate class per each request or an enum.
Separate class for each request
class MyRequest: Request {
typealias ResponseObjectType = MyExpectedResponse
typealias ErrorType = MyUnexpectedError
let path = "my/path/to/backend" // Required
let methong = .get // Optional
let queryParams = [("key", "value")] // Optional
let body = .params(["key", "value"]) // Optional
let headers = [RequestHeaderKey("key"): "value", .contentType: "application/json"] // Optional
let timeout = 60 // Optional, by defauld will be picked from Endpoint
let endpoint = myEndpoint // Optional, by default will be a Endpoint.default
}
This is just an example and probably you will not have all parameters required and static. To have dynamicaly created request - you can just use initializers that willl be taking dynamic parameters required for request.
As an example - some dynamic bodyParams or dynamic path.
Default request
You can use buit in class DefaultRequest to perform requests without a need to create a separate class.
NerdzNetworking library also provide an easy way of creation and execution of multipart form-data requests. You just need to implement MultipartFormDataRequest instead of Request protocol or use DefaultMultipartFormDataRequest class.
In addition to Request fields you will need to provide files field of MultipartFile protocol instances. You can implement this protocol or use DefaultMultipartFile class.
class MyMultipartRequest: MultipartFormDataRequest {
// Same fields as in Request example
let files: [MultipartFile] = [
DefaultMultipartFile(resource: fileData, mime: .image(.png), fileName: "avatar1"),
DefaultMultipartFile(resource: fileUrl, mime: .audio(.mp4), fileName: "song"),
DefaultMultipartFile(resource: filePath, mime: .image(.jpeg), fileName: "avatar2")
]
}
Request execution
To exucute request you can use next constructions:
myRequest.execute(): will execute myRequest on Endpoint.default
myRequest.execute(on: myEndpoint): will execute myRequest on myEndpoint
myEndpoint.execute(myRequest): will execute myRequest on myEndpoint
Handling execution process
To handle execution process you can use futures-style methods after execute method called. (every method is optional, so use only those you really need)
myRequest
.execute()
.responseOn(.main) // Response will be returned in `.main` queue
.retryOnFail(false) // If this request will fail - system will not try to rerun it again
.onStart { requestOperation in
// Will be called when request will start and return request operation that allow to control request during the execution
}
.onProgress { progress in
// Will provide a progress of request execution. Useful for multipart uploading requests
}
.onSuccess { response in
// Will return a response object specified in request under `ResponseType`
}
.onFail { error in
// Will return `ErrorResponse` that might contain `ErrorType` specified in request
}
.onDebug { info in
// Will return `DebugInfo` that contain a list of useful information for debugging request failure
}
Mapping
For now NerdzNetworking library supports only native Codable mapping. Tutorial.
Response converters
NerdzNetworking supports response converters that might convert response before mapping process. Might be useful if case you need to adopt response data to internal model, or to bypass parent object to map only chileds.
ResponseJsonConverter
You can also provide a response converters to convert some unpropertly returned responses beаore mapping into expected response starts. The responsible protocol for this is ResponseJsonConverter.
Response converter should be specified in Request class under responseConverter(success) or/and errorConverter(fail) fields.
You can have your own converters that implement ResponseJsonConverter protocol, or use built in implementations: KeyPathResponseConverter, ClosureResponseConverter.
KeyPathResponseConverter
KeyPathResponseConverter allow you to pull a data from JSON chileds node by specific path to the node.
class MyRequest: Request {
var responseConverter: ResponseJsonConverter {
KeyPathResponseConverter(path: "path/to/node")
}
var errorConverter: ResponseJsonConverter {
KeyPathResponseConverter(path: "path/to/error")
}
}
ClosureResponseConverter
ClosureResponseConverter allow you to provide custom convertation by closure. You will need to provide a closure that takes Any and return Any after convertation.
class MyRequest: Request {
var responseConverter: ResponseJsonConverter {
ClosureResponseConverter { response in
// Return converted response for success response
}
}
var errorConverter: ResponseJsonConverter {
ClosureResponseConverter { response in
// Return converted response for error response
}
}
}
Custom ResponseJsonConverter
You can implement your ovn converter by implementing ResponseJsonConverter protocol.
class MyResponseConverter: ResponseJsonConverter {
func convertedJson(from json: Any) throws -> Any {
// Provide convertation and return converted code
}
}
Installation
CocoaPods
You can use CocoaPods dependency manager to install NerdzNetworking.
In your Podfile spicify:
Registering closure that will be triggered when request starts processing
You can call this method several times, and every registered closure will be triggered
Name
Type
Default value
Description
closure
() -> Void
Triggered closure
func calncel()
Cancel request processing
@ Request protocol
TYPE: protocol
Protocol that represents a single request. You can imlement this protocol and then execute it. You can use DefaultRequest struct, that already implements this protocol, for executing requests.
associatedtype
Name
Type
Accessibility
Description
ResponseObjectType
ResponseObject
A type of expected response from server. It should implement ResponseObject protocol
ErrorType
ServerError
A type of expected error from server. It should implement ServerError protocol
Properties
Name
Type
Accessibility
Description
path
String
getrequired
A request path
method
HTTPMethod
getrequired
A request method
queryParams
[(String, String)]
getoptional
A request query parameters represented as an array of touples to save order
body
RequestBody?
getoptional
A request body
headers
[RequestHeaderKey: String]
getoptional
A request specific headers. Will be used is addition to headers from Endpoint
timeout
TimeInterval
getoptional
A request timeout. If not specified - will be used default from Endpoint
responseConverter
ResponseJsonConverter?
getoptional
A successful response converter. Will be converted before mapping into a ResponseObjectType
errorConverter
ResponseJsonConverter?
getoptional
An error response converter. Will be converted before mapping into a ErrorType
endpoint
Endpoint?
getoptional
An endpoint that will be used for execution
decoder
JSONDecoder?
getoptional
A JSON response decoder that will be used for decoding response. In case not provided - decoder from Endpoint will be used
Initialize DefaultRequest object with all possible parameters
Name
Type
Default value
Description
path
String
-
A request path
method
HTTPMethod
-
A request method
queryParams
[(String, String)]
[]
A request query parameters represented as an array of touples to save order
bodyParams
[String: Any]
[:]
A request body params
headers
[RequestHeaderKey: String]
[:]
A request specific headers. Will be used is addition to headers from Endpoint
timeout
TimeInterval
nil
A request timeout. If not specified - will be used default from Endpoint
responseConverter
ResponseJsonConverter?
nil
A successful response converter. Will be converted before mapping into a ResponseObjectType
errorConverter
ResponseJsonConverter?
nil
An error response converter. Will be converted before mapping into a ErrorType
endpoint
Endpoint?
nil
An endpoint that will be used for execution
decoder
JSONDecoder?
nil
A JSON response decoder that will be used for decoding response. In case not provided - decoder from Endpoint will be used
@ MultipartFormDataRequest protocol
TYPE: protocol
INHERITS: Request protocol
Protocol that represents a multipart form-data request. Protocol inherits Request protocol, and adding files property on top. So mostly it is the same as Request protocol. You can use DefaultMultipartFormDataRequest struct, that already implements this protocol, for executing multipart requests.
associatedtype
Name
Type
Accessibility
Description
ResponseObjectType
ResponseObject
A type of expected response from server. It should implement ResponseObject protocol
ErrorType
ServerError
A type of expected error from server. It should implement ServerError protocol
Properties
Name
Type
Accessibility
Description
path
String
getrequired
A request path
method
HTTPMethod
getrequired
A request method
queryParams
[(String, String)]
getoptional
A request query parameters represented as an array of touples to save order
body
RequestBody?
getoptional
A request body
headers
[RequestHeaderKey: String]
getoptional
A request specific headers. Will be used is addition to headers from Endpoint
timeout
TimeInterval
getoptional
A request timeout. If not specified - will be used default from Endpoint
responseConverter
ResponseJsonConverter?
getoptional
A successful response converter. Will be converted before mapping into a ResponseObjectType
errorConverter
ResponseJsonConverter?
getoptional
An error response converter. Will be converted before mapping into a ErrorType
endpoint
Endpoint?
getoptional
An endpoint that will be used for execution
decoder
JSONDecoder?
getoptional
A JSON response decoder that will be used for decoding response. In case not provided - decoder from Endpoint will be used
files
[MultipartFile]
getrequired
A list of files that needs to be processed with request
NerdzNetworking
NerdzNetworking
is a wrapper on top ofURLSession
andURLRequest
to simplify creating and managing network requests written onSwift
language.Example
You need to define request.
And then just use it. (each call is optional and can be skipped if needed)
Ideology
Structure
The main ideology for creating
NerdzNetworking
library was to maximaly split networking into small pieces. The ideal scenario would be to have a separate class/structure per each request. This should help easily navigate and search information for specific request in files structure.Strong typization
Another flow that library truing to follow - predefined options for using and simplicity of using. We are trying to use generic types on top of protocols, as well as enumerations instead of raw values. As an example - predefined headers like
Content-Type
orAccept
. Instead of giving a possibility to put any value, we have defined and enumerations that limits possbie input only to predefined scenarios like.application(.json)
. To make it simple, previously mentioned headers already have.application(.json)
value preselected for you, so in case you are using standard REST API - everything ready from the box.Tutorial
Endpoint setup
First of all you need to setup your endpoint that will be used later on for executing requests. To do that - you should be using
Endpoint
class.Endpoint
class will collect all general settings for performing requests. You can change any parameter at any time you want.After creating your endpoint, you can mark it as a
default
, so every request will pick it up automatically.You can change
default
endpoint based on configuration or environment you need.Request creation
To create a request you should implement
Request
protocol. You can or have separate class per each request or anenum
.Separate class for each request
This is just an example and probably you will not have all parameters required and static. To have dynamicaly created request - you can just use initializers that willl be taking dynamic parameters required for request. As an example - some dynamic bodyParams or dynamic path.
Default request
You can use buit in class
DefaultRequest
to perform requests without a need to create a separate class.Multipart request
NerdzNetworking
library also provide an easy way of creation and execution of multipart form-data requests. You just need to implementMultipartFormDataRequest
instead ofRequest
protocol or useDefaultMultipartFormDataRequest
class.In addition to
Request
fields you will need to providefiles
field ofMultipartFile
protocol instances. You can implement this protocol or useDefaultMultipartFile
class.Request execution
To exucute request you can use next constructions:
myRequest.execute()
: will executemyRequest
onEndpoint.default
myRequest.execute(on: myEndpoint)
: will executemyRequest
onmyEndpoint
myEndpoint.execute(myRequest)
: will executemyRequest
onmyEndpoint
Handling execution process
To handle execution process you can use futures-style methods after
execute
method called. (every method is optional, so use only those you really need)Mapping
For now
NerdzNetworking
library supports only nativeCodable
mapping. Tutorial.Response converters
NerdzNetworking
supports response converters that might convert response before mapping process. Might be useful if case you need to adopt response data to internal model, or to bypass parent object to map only chileds.ResponseJsonConverter
You can also provide a response converters to convert some unpropertly returned responses beаore mapping into expected response starts. The responsible protocol for this is
ResponseJsonConverter
.Response converter should be specified in
Request
class underresponseConverter
(success) or/anderrorConverter
(fail) fields.You can have your own converters that implement
ResponseJsonConverter
protocol, or use built in implementations:KeyPathResponseConverter
,ClosureResponseConverter
.KeyPathResponseConverter
KeyPathResponseConverter
allow you to pull a data fromJSON
chileds node by specificpath
to the node.ClosureResponseConverter
ClosureResponseConverter
allow you to provide custom convertation by closure. You will need to provide aclosure
that takesAny
and returnAny
after convertation.Custom
ResponseJsonConverter
You can implement your ovn converter by implementing
ResponseJsonConverter
protocol.Installation
CocoaPods
You can use CocoaPods dependency manager to install
NerdzNetworking
. In yourPodfile
spicify:Swift Package Manager
To add NerdzNetworking to a Swift Package Manager based project, add:
Docummentation
@
Endpoint
classClass that represents and endpoint with all settings for requests execution. You need to create at least one instance to be able to execute requests.
Properties
Endpoint.default
Endpoint
static
read-write
baseUrl
URL
readonly
decoder
JSONDecoder?
responseQueue
DispatchQueue?
retryingCount
Int
observation
ObservationManager
requestRetrying
RequestRetryingManager
sessionConfiguration
URLSessionConfiguration
readonly
URLSession
headers
[RequestHeaderKey: String]
read-write
Methods
Initializing with all parameters
baseUrl
URL
decoder
JSONDecoder?
nil
responseQueue
DispatchQueue
nil
sessionConfiguration
URLSessionConfiguration
.default
URLSession
retryingCount
Int
1
headers
[RequestHeaderKey: String]
[:]
Executing request on current endpoint
request
Request
Return cURL string representation for provided request
Sets current instance as a
default
for future execution@
ExecutionOperation<T>
classGENERICS:
T: Request
A class that represents request operation execution parameters like
queue
and completions likeonSuccess
.Properties
request
T
readonly
Methods
Setting up a queue on what all completions will be called. By default
main
queue will be usedqueue
DispatchQueue
Setting up a decoder that will be used for decoding JSON response or error
decoder
JSONDecoder
Setting up retrying count for request failings. By default it is equal to
1
retryingCount
Int
ResponseSuccessCallback = (T.ResponseObjectType) -> Void
Registering closure that will be called if request was successful
closure
(T.ResponseObjectType) -> Void
FailCallback = (ErrorResponse<T.ErrorType>) -> Void
Registering closure that will be called if request fails
closure
(ErrorResponse<T.ErrorType>) -> Void
ProgressCallback = (Double) -> Void
Registering closure that will return request processing progress
closure
(Double) -> Void
DebugCallback = (DebugInfo) -> Void
Registering closure that will return request & response information for debugging
closure
(DebugInfo) -> Void
StartCallback = () -> Void
Registering closure that will be triggered when request starts processing
closure
() -> Void
Cancel request processing
@
Request
protocolTYPE:
protocol
Protocol that represents a single request. You can imlement this protocol and then execute it. You can use
DefaultRequest
struct, that already implements this protocol, for executing requests.associatedtype
ResponseObjectType
ResponseObject
ResponseObject
protocolErrorType
ServerError
ServerError
protocolProperties
path
String
get
required
method
HTTPMethod
get
required
queryParams
[(String, String)]
get
optional
body
RequestBody?
get
optional
headers
[RequestHeaderKey: String]
get
optional
Endpoint
timeout
TimeInterval
get
optional
Endpoint
responseConverter
ResponseJsonConverter?
get
optional
ResponseObjectType
errorConverter
ResponseJsonConverter?
get
optional
ErrorType
endpoint
Endpoint?
get
optional
decoder
JSONDecoder?
get
optional
Endpoint
will be usedMethods
Executing current request on provided endpoint
endpoint
Endpoint
Executing current request on
Endpoint.default
instance@
DefaultRequest
structTYPE:
struct
IMPLEMENT:
Request
A default implementation of
Request
protocol that can be used for executing requests without creation of extra classGenerics
Response
ResponseObject
Error
ServerError
Properties
path
String
read-write
method
HTTPMethod
read-write
queryParams
[(String, String)]
read-write
body
RequestBody?
read-write
headers
[RequestHeaderKey: String]
read-write
Endpoint
timeout
TimeInterval
read-write
Endpoint
responseConverter
ResponseJsonConverter?
read-write
ResponseObjectType
errorConverter
ResponseJsonConverter?
read-write
ErrorType
endpoint
Endpoint?
read-write
decoder
JSONDecoder?
read-write
Endpoint
will be usedMethods
Initialize
DefaultRequest
object with all possible parameterspath
String
method
HTTPMethod
queryParams
[(String, String)]
[]
bodyParams
[String: Any]
[:]
headers
[RequestHeaderKey: String]
[:]
Endpoint
timeout
TimeInterval
nil
Endpoint
responseConverter
ResponseJsonConverter?
nil
ResponseObjectType
errorConverter
ResponseJsonConverter?
nil
ErrorType
endpoint
Endpoint?
nil
decoder
JSONDecoder?
nil
Endpoint
will be used@
MultipartFormDataRequest
protocolTYPE:
protocol
INHERITS:
Request
protocolProtocol that represents a multipart form-data request. Protocol inherits
Request
protocol, and adding files property on top. So mostly it is the same asRequest
protocol. You can useDefaultMultipartFormDataRequest
struct, that already implements this protocol, for executing multipart requests.associatedtype
ResponseObjectType
ResponseObject
ResponseObject
protocolErrorType
ServerError
ServerError
protocolProperties
path
String
get
required
method
HTTPMethod
get
required
queryParams
[(String, String)]
get
optional
body
RequestBody?
get
optional
headers
[RequestHeaderKey: String]
get
optional
Endpoint
timeout
TimeInterval
get
optional
Endpoint
responseConverter
ResponseJsonConverter?
get
optional
ResponseObjectType
errorConverter
ResponseJsonConverter?
get
optional
ErrorType
endpoint
Endpoint?
get
optional
decoder
JSONDecoder?
get
optional
Endpoint
will be usedfiles
[MultipartFile]
get
required
Methods
Executing current request on provided endpoint
endpoint
Endpoint
Executing current request on
Endpoint.default
instance@
DefaultMultipartFormDataRequest
structTYPE:
struct
IMPLEMENT:
MultipartFormDataRequest
A default implementation of
MultipartFormDataRequest
protocol that can be used for executing multipart requests without creation of extra classGenerics
Response
ResponseObject
Error
ServerError
Properties
path
String
read-write
method
HTTPMethod
read-write
queryParams
[(String, String)]
read-write
body
RequestBody?
read-write
headers
[RequestHeaderKey: String]
read-write
Endpoint
timeout
TimeInterval
read-write
Endpoint
responseConverter
ResponseJsonConverter?
read-write
ResponseObjectType
errorConverter
ResponseJsonConverter?
read-write
ErrorType
endpoint
Endpoint?
read-write
decoder
JSONDecoder?
read-write
Endpoint
will be usedfiles
[MultipartFile]
read-write
Methods
Initialize
DefaultMultipartFormDataRequest
object with all possible parameterspath
String
method
HTTPMethod
queryParams
[(String, String)]
[]
bodyParams
[String: Any]
[:]
headers
[RequestHeaderKey: String]
[:]
Endpoint
timeout
TimeInterval
nil
Endpoint
responseConverter
ResponseJsonConverter?
nil
ResponseObjectType
errorConverter
ResponseJsonConverter?
nil
ErrorType
endpoint
Endpoint?
nil
decoder
JSONDecoder?
nil
Endpoint
will be usedfiles
[MultipartFile]
[]
@
DefaultMultipartFile
structA default implementation of
MultipartFile
protocol that you can use in case you do not want to create additional class for sending multipart requestProperties
fileName
String
read-write
mime
MimeType
read-write
resource
MultipartResourceConvertable
read-write
String
,Data
,URL
,InputStream
Methods
Initialize
DefaultMultipartFile
object with all possible parametersresource
MultipartResourceConvertable
mime
MimeType
fileName
String
String
,Data
,URL
,InputStream
@
ServerError
protocolA protocol that represents an error returned from server
Properties
message
String
get
required
Supported types
String
Optional
@
HTTPMethod
enumTYPE:
enum
INHERITS:
String
An enum that represents a request http method.
.get
GET
http method.post
POST
http method.put
PUT
http method.delete
DELETE
http method.path
PATH
http method@
RequestBody
enumTYPE:
enum
An enum that represents different types of request body
.raw
value: Data
.string
value: String
.params
value: [String: Any]
@
DebugInfo
structTYPE:
struct
Represents an information for debugging networking request.
Properties
sessionConfiguration
URLSessionConfiguration
readonly
URLSession
configurationrequest
URLRequest
readonly
URLRequest
that were built for executiondataResponse
Data?
readonly
Data
formaturlResponse
HTTPURLResponse
readonly
errorResponse
Error?
readonly
requestDuration
TimeInterval
readonly
cURL
String?
readonly
stringResponse
String?
readonly
String
formatjsonResponse
Any?
readonly
JSON
formatAPI To be continued…
Next steps
Combine
supportLicense
This code is distributed under the MIT license. See the
LICENSE
file for more info.