24. Mar 2023
iOSExploring our iOS toolbox - Handling network requests in Swift
While Swift provides built-in networking capabilities, there are some disadvantages to using them, such as lack of standardisation, limited flexibility and challenges in maintaining and updating the codebase over time, especially as the number of endpoints grows. So, is there a more efficient way to accomplish this task?
Well, great news! You can handle network requests more conveniently by using our GoodNetworking swift package. Let’s begin, shall we?
Preparing API & Endpoints
In this example, we will retrieve Star Wars heroes using the free Star Wars API.
To begin, we must first define the base URL for the ApiServer
that conforms to the type String
. We will also need to define an endpoint that conforms to the GREndpointManager
protocol.
This endpoint will specify the path, method, parameters, headers, encoding, and URL for your network request.
You can define multiple endpoints for different network requests within your application.
import GoodNetworking
import Combine
import Alamofire
// define the base url of the api server
enum ApiServer: String {
case base = "<https://swapi.dev/api/>"
}
// define Endpoint for fetching the StarWars heroes
// later on you can add edpoints for StarWars Planets or Starships
enum Endpoint: GREndpointManager {
case hero(id: Int)
var path: String {
switch self {
case .hero(let id):
return "people/\\(id)"
}
}
var method: HTTPMethod { .get }
var parameters: EndpointParameters? { nil }
var headers: HTTPHeaders? {nil}
var encoding: ParameterEncoding { JSONEncoding.default }
func asURL(baseURL: String) throws -> URL {
var url = try baseURL.asURL()
url.appendPathComponent(path)
return url
}
}
Prepare API response
Next, we have to specify the expected response from the API. According the StarWars API, we expect a response with the name
attribute of type String
. We can define a struct with this attribute as follows:
// define the request response
struct HeroResponse: Decodable {
let name: String
}
Now we have almost everything ready, we can setup our GRSession to make requests.
Making a network request
Once we have endpoint defined, we can create an instance of the GRSession
.
**GRSession
**is based on Alamofire Session
class and ****manages network requests in client apps within its URLSession.
It takes two generic parameters:
- (one conforming to
GREndpointManager
) - the Endpoint we defined earlier - (one conforming to
RawRepresentable
withRawValue
ofString
) - our ApiServer
Here's an example of how you can use the GRSession
class to make a network request:
// Create a GRSession object
let session: GRSession<Endpoint, ApiServer> = GRSession(
baseURL: ApiServer.base
configuration: .default
)
// fetch a hero with specified ID
func fetchHero(heroId: Int) -> AnyPublisher<HeroResponse, Never> {
return session.request(endpoint: .hero(id: heroId))
.goodify()
.replaceError(with: HeroResponse(name: "unknown"))
.eraseToAnyPublisher()
}
The result of **fetchHero**
function is a publisher so you can continue chaining Combine functions.
To show fetched StarWars hero’s name inside a label, simply call the function and use Combine’s sink
or assign
methods to achieve this goal.
// define heroLabel to show hero Name
let heroLabel = UILbel()
// define subscriber
var cancellable: AnyCancellable?
// subcribe to result of network request
cancellable = fetchHero(heroId: Int.random(1...66))
.map { response in response.name }
.removeDuplicates()
.assing(to: \\.text, on: heroLabel, ownership: .weak)
Congrats!
You have gone through the process of making Network Request using GoodNetworking package.
Now you may be wondering how it works under the hood inside the app. Luckily, the package comes with a sample that you can explore to gain a deeper understanding of its functionality.
If you found this package useful, be sure to check out our other packages. Who knows, you might just find another gem that can help take your app to the next level!