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
- Pure Swift
- Intuitive, simple API
- Protocol-oriented implementation
- Extensive Unit Tests coverage
- Support for reactive programming (
Combine
)
- Support for modern Swift concurrency (
async/await
)
- Extendable with configurable actions
Integration
Requirements
Adding module to your app
NgNetworkModule is available through Swift Package Manager.
- 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) { ... }
Defining custom pre/post execution request actions:
/// 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.
Description: The requested endpoint cannot be found.
Solution: Ensure the request path is correct.
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.
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!
Authors & project maintainers
See also the list of contributors who participated in this project.
License
This project is licensed under the MIT License.
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