Welcome to the Netguru Networking Module. NgNetworkModule is simple, highly configurable iOS and MacOS module to execute network calls and parse received answers.
Main Features
Pure Swift
Intuitive, simple API
Protocol-oriented implementation
Extensive Unit Tests coverage
Support for reactive programming (Combine)
Support for modern Swift concurrency (async/await)
Select File > Swift Packages > Add Package Dependency.
Enter the repository URL.
Wait until the dependencies are resolved.
```
Using NgNetworkModule
Available API variants:
The module is available in 3 variants:
Network Module Core - a base module, leveraging callback-based API to execute a network request and return an answer in a callback.
import NgNetworkModuleCore
Reactive API (Combine) - a reactive API emitting an answer as an event, or an error. Requires importing NgNetworkModuleCore to work!
import ReactiveNgNetworkModule
Concurrent API (async/await) - an API utilising modern Swift concurrency. Requires importing NgNetworkModuleCore to work!
import ConcurrentNgNetworkModule
Creating network module:
import NgNetworkModuleCore
(optionally) import ReactiveNgNetworkModule / ConcurrentNgNetworkModule
...
let baseURL = URL(string: "https://your.base.url")!
let requestBuilder = DefaultRequestBuilder(baseURL: baseURL)
let networkModule = DefaultNetworkModule(requestBuilder: requestBuilder)
Creating and using automatic request / response parsing:
In order to parse correctly, the request description must conform to NetworkRequest protocol:
struct SampleGetNetworkRequest: NetworkRequest {
let path = "/welcome"
let method = NetworkRequestType.get
}
or
struct SamplePostNetworkRequest: NetworkRequest {
let method = NetworkRequestType.post
let path = "/welcome"
let body: Encodable? = ["foo": "bar"]
}
or
struct SamplePostNetworkRequest2: NetworkRequest {
let method = NetworkRequestType.post
let path = "/welcome2"
let bodyData: Data? = Data()
}
Having defined such a request, you may use convenience API to encode and call it, (optionally) decoding received response:
struct ExpectedResponse: Decodable {}
...
let request = SampleGetRequest()
...
networkModule.performAndDecode(request: request, responseType: ExpectedResponse.self)
switch result {
case let .success(response):
// Handle decoded response here
case let .failure(error):
// Handle error here
}
}
Available APIs
Default API - a classic API to execute a network request and return an answer in a callback:
networkModule.perform(request: networkRequest) { result in
networkModule.perform(urlRequest: urlRequest) { result in
networkModule.performAndDecode(urlRequest: urlRequest, responseType: ExpectedResponse.self) { result in
networkModule.performAndDecode(request: networkRequest, responseType: ExpectedResponse.self) { result in
switch result {
case let .success(response):
// Handle response here
case let .failure(error):
// Handle error here
}
}
Reactive API (Combine) - a reactive API emitting an answer as an event, or an error: No need to add .receive(on: DispatchQueue.main) as it is guaranteed by the NgNetworkModuleCore design.
perform(request: networkRequest)
perform(urlRequest: urlRequest)
performAndDecode(request: networkRequest, responseType: ExpectedResponse.self)
performAndDecode(urlRequest: urlRequest, responseType: ExpectedResponse.self)
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
// Handle completion here (optionally)
case let .failure(error):
// Handle error here
}
},
receiveValue: { response in
// Handle response here
})
.store(in: &cancellables)
Concurrent API (async/await) - an API utilising modern Swift concurrency:
do {
let response = try await networkModule.perform(request: networkRequest)
let response = try await networkModule.perform(urlRequest: urlRequest)
let response = try await networkModule.performAndDecode(urlRequest: urlRequest, responseType: ExpectedResponse.self)
let response = try await networkModule.performAndDecode(request: networkRequest, responseType: ExpectedResponse.self)
// Handle response here
} catch {
// Handle error here
}
Advanced use cases
Adding authentication token to the request:
let accessTokenProvider = ... // Implement AuthenticationTokenProvider or just use String
let accessTokenHeaderName = "Authorization"
addTokenAction = AddAuthenticationTokenNetworkModuleAction(
authenticationTokenProvider: accessTokenProvider,
authenticationTokenHeaderFieldName: accessTokenHeaderName
)
...
let networkModule = DefaultNetworkModule(
requestBuilder: requestBuilder,
actions: [addTokenAction] // <-- Include additional Actions here
)
...
networkModule.perform(request: networkRequest) { ... }
/// An Action extracting an updated Access Token from a received Network Response:
struct RetrieveAccessTokenAction: NetworkModuleAction {
private let accessTokenHeaderName = "Access-token"
func performBeforeExecutingNetworkRequest(request: NetworkRequest?, urlRequest: inout URLRequest) {
// Noop
}
func performAfterExecutingNetworkRequest(request: NetworkRequest?, networkResponse: NetworkResponse) {
guard let headerFields = networkResponse.networkResponse.allHeaderFields,
let accessToken = headerFields[accessTokenHeaderName] as? String else {
return
}
// Save retrieved access token
}
}
Adding the Action to the Network Module:
let action = RetrieveAccessTokenAction()
let networkModule = DefaultNetworkModule(
requestBuilder: requestBuilder,
actions: [action] // <-- Include additional Actions here
)
Error handling
NetworkError.requestParsingFailed\
Description: The request builder failed to parse the NetworkRequest structure into an URLRequest. Solution: Ensure that the non-trivial request properties (especially parameters, body or bodyData) are properly defined.
NetworkError.responseParsingFailed\
Description: Although the request succeeded, the NetworkModule failed to decoded received response into a provided data type. Solution: Ensure an expected response type matches what is returned from the backend.
NetworkError.noResponseData\
Description: Although the request succeeded, the received response contained no data to decode. Solution: Ensure the endpoint you try to call is supposed to return a non-empty response.
NetworkError.serverError(code:message:)\
Description: The request caused a server error. Solution: Report the error to the backend team.
NetworkError.notFound\
Description: The requested endpoint cannot be found. Solution: Ensure the request path is correct.
NetworkError.forbidden\
Description: The requested endpoint requires authentication. Solution: Ensure that you included a valid Access Token in the request.
NetworkError.unauthorized\
Description: The provided access token does not allow you to access the requested resource. Solution: Reach out to the backend team and verify access level for the resource.
NetworkError.invalidRequest(code:message:)\
Description: Any other request-related error (4XX error code). Solution: Ensure the outgoing request is according to the backend specification.
NetworkError.cancelled\
Description: The request has been cancelled by the user. Solution: Not treated as an error!
Sample content
Running sample application
There is a sample application showcasing the NgNetworkModule versatility. It can be found in NetworkModuleSampleApp folder and opened using the Xcode project file.
The app uses SwiftFormat dependency (obtained from the NgNetworkModule) to ensure proper code formatting. The application is based on free API service for Final Space animated series. It allows you to go through the list of episodes, see the episode details and characters that appeared there. It is written 100% in SwiftUI, targeting iOS 16.
Points of interests:
HomeScene - a main app view, allowing to select which of the APIs exposed by the NgNetworkModule (classic, reactive or async/await) should be used to obtain data from the Final Space API service.
EpisodeListViewModel - a view model for EpisodeListScene, showcasing obtaining, parsing and providing episodes list data to the view.
EpisodeViewModel - a view model for EpisodeScene, showcasing obtaining, parsing and providing data necessary to render episode details view (mostly character data).
Running sample playground
Alternatively, within NetworkModuleSampleApp there is a Playground folder containing a playground file also showcasing the NgNetworkModule API. It uses several, internal source files, e.g. FetchCharacterRequest and FetchCharacterResponse to increase code readability.
Contributing
If you would like to help us improve the module, don’t hesitate to contribute!
Netguru Networking Module
Welcome to the Netguru Networking Module.
NgNetworkModule is simple, highly configurable iOS and MacOS module to execute network calls and parse received answers.
Main Features
Combine
)async/await
)Integration
Requirements
Adding module to your app
NgNetworkModule is available through Swift Package Manager.
Using NgNetworkModule
Available API variants:
The module is available in 3 variants:
Combine
) - a reactive API emitting an answer as an event, or an error. Requires importingNgNetworkModuleCore
to work!async/await
) - an API utilising modern Swift concurrency. Requires importingNgNetworkModuleCore
to work!Creating network module:
Creating and using automatic request / response parsing:
In order to parse correctly, the request description must conform to
NetworkRequest
protocol:or
or
Having defined such a request, you may use convenience API to encode and call it, (optionally) decoding received response:
Available APIs
Combine
) - a reactive API emitting an answer as an event, or an error:No need to add
.receive(on: DispatchQueue.main)
as it is guaranteed by theNgNetworkModuleCore
design.async/await
) - an API utilising modern Swift concurrency:Advanced use cases
Adding authentication token to the request:
Defining custom pre/post execution request actions:
Adding the Action to the Network Module:
Error handling
NetworkError.requestParsingFailed
\Description: The request builder failed to parse the
NetworkRequest
structure into anURLRequest
.Solution: Ensure that the non-trivial request properties (especially
parameters
,body
orbodyData
) are properly defined.NetworkError.responseParsingFailed
\Description: Although the request succeeded, the
NetworkModule
failed to decoded received response into a provided data type.Solution: Ensure an expected response type matches what is returned from the backend.
NetworkError.noResponseData
\Description: Although the request succeeded, the received response contained no data to decode.
Solution: Ensure the endpoint you try to call is supposed to return a non-empty response.
NetworkError.serverError(code:message:)
\Description: The request caused a server error.
Solution: Report the error to the backend team.
NetworkError.notFound
\Description: The requested endpoint cannot be found.
Solution: Ensure the request path is correct.
NetworkError.forbidden
\Description: The requested endpoint requires authentication.
Solution: Ensure that you included a valid Access Token in the request.
NetworkError.unauthorized
\Description: The provided access token does not allow you to access the requested resource.
Solution: Reach out to the backend team and verify access level for the resource.
NetworkError.invalidRequest(code:message:)
\Description: Any other request-related error (4XX error code).
Solution: Ensure the outgoing request is according to the backend specification.
NetworkError.cancelled
\Description: The request has been cancelled by the user.
Solution: Not treated as an error!
Sample content
Running sample application
There is a sample application showcasing the NgNetworkModule versatility. It can be found in
NetworkModuleSampleApp
folder and opened using the Xcode project file. The app usesSwiftFormat
dependency (obtained from the NgNetworkModule) to ensure proper code formatting.The application is based on free API service for Final Space animated series. It allows you to go through the list of episodes, see the episode details and characters that appeared there. It is written 100% in SwiftUI, targeting iOS 16. Points of interests:
HomeScene
- a main app view, allowing to select which of the APIs exposed by the NgNetworkModule (classic, reactive or async/await) should be used to obtain data from the Final Space API service.EpisodeListViewModel
- a view model forEpisodeListScene
, showcasing obtaining, parsing and providing episodes list data to the view.EpisodeViewModel
- a view model forEpisodeScene
, showcasing obtaining, parsing and providing data necessary to render episode details view (mostly character data).Running sample playground
Alternatively, within
NetworkModuleSampleApp
there is aPlayground
folder containing a playground file also showcasing the NgNetworkModule API.It uses several, internal source files, e.g.
FetchCharacterRequest
andFetchCharacterResponse
to increase code readability.Contributing
If you would like to help us improve the module, don’t hesitate to contribute!
Authors & project maintainers
See also the list of contributors who participated in this project.
License
This project is licensed under the MIT License.
Related repositories