Skip to content

Repository files navigation

Logo

GoodNetworking

iOS VersionSwift VersionSupported devicesContains TestDependency Manager

GoodNetworking is a powerful Swift library designed to simplify HTTP networking by leveraging the capabilities of Swift’s concurrency model, Combine, and Alamofire. It provides a flexible and easy-to-use API for handling complex network operations, making it easier to perform tasks such as sending requests, downloading, and uploading data.

Key Features

• NetworkSession: A powerful and flexible mechanism for managing HTTP sessions, providing built-in support for sending requests, downloading, and uploading files. It uses a session provider to handle session configuration and lifecycle management.
• Request Validation: Support for validating responses using custom validation providers.
• Flexible Endpoint Handling: Define your own endpoint configurations with ease, allowing for a clean and maintainable way to manage network requests.

Documentation

Check out GoodNetworking documentation here

Installation

Swift Package Manager

Create a Package.swift file and add the package dependency into the dependencies list. Or to integrate without package.swift add it through the Xcode add package interface.

import PackageDescription
letpackage=Package(
name:"SampleProject",
dependencies:[.package(url:"https://github.com/GoodRequest/GoodNetworking" from:"addVersion")]
targets:[.target(
name:"Sample Target",
dependencies:[.product(name:"GoodNetworking",package:"GoodNetworking"),],),])

Usage

Quick start session

Create an session actor best handled with dependency injection pattern

extensionNetworkSession{staticvarsampleSession: NetworkSession(baseUrl:"https://reqres.in/api")
}

Create a sample endpoint

import GoodNetworking
enumSampleEndpoint:Endpoint{case singleUser(id:Int)varpath:String{switchself{case.singleUser(let id):"users/\(id)"}}

Create a viewmodel where you call the async function to fetch the user.

@MainActorfinalclassUserProfileViewModel{varuserProfile:Result<User,Error>!func fetchUser()async{do{letresult:User=tryawaitNetworkSession.sampleSession.request(endpoint:SampleEndpoint.singleUser(id:1))
userProfile =.success(result)}catch{
userProfile =.failure(error)}}}

Rich sample session

Ofcourse the is much more

Our session works with alamofire session wrapping it easy to use.

Initilize session

There are 3 different initializers for our session

You can initilize with the parameter baseUrlProvider comforming to BaseUrlProviding protocol serving as baseUrl to your API calls where String is overloaded comform to it so any string baseUrl will work, but you can also provide more intricate logic that handles runtime baseUrl swapping.

The next parameter sessionProvider similarly to the first one as by default works with a DefaultSessionProvider that holds just a session with a default UrlSessionConfiguration so you can omit it, but you can also user DefaultSessionProvider's configuration init parameter and provide your own NetworkSessionConfiguration. The other initializers serve as convenience to initializer these 2 values indirectly through other types.

Default

publicinit(
baseUrlProvider:BaseUrlProviding?=nil,
sessionProvider:NetworkSessionProviding=DefaultSessionProvider(configuration:.default)){self.baseUrlProvider = baseUrlProvider
self.sessionProvider = sessionProvider
}

Backwards compatibility for our older projects

publicinit(
baseUrl:BaseUrlProviding?=nil,
configuration:NetworkSessionConfiguration=.default
){self.baseUrlProvider = baseUrl
self.sessionProvider =DefaultSessionProvider(configuration: configuration)}

Customizable session if you need more than just the basic session parameters provided by NetworkSessionConfiguration

publicinit(
baseUrlProvider:BaseUrlProviding?=nil,
session:Alamofire.Session){self.baseUrlProvider = baseUrlProvider
self.sessionProvider =DefaultSessionProvider(session: session)}

Requests in views

With this library you can directly call the request and update the state for a more straithforward way to update the UI

import Alamofire
import GoodNetworking
import SwiftUI
structUserScreen:View{@Stateprivatevaruser=Resource(session:.sampleSession, remote:RemoteUser.self)letuserId:Intvarbody:someView{ScrollView{VStack(spacing:24){userView(user: user.state)}.padding()}.refreshable{try?await user.read(forceReload:true)}.task{try?await user.read(request:UserRequest(id: userId))}}privatefunc loadingView()->someView{HStack(spacing:8){ProgressView()Text("Loading...")}}@ViewBuilderprivatefunc userView(user:ResourceState<User,NetworkError>)->someView{switch user {case.idle:Text("Resource idle")case.loading:HStack(spacing:8){ProgressView()Text("Loading...")}case.failure(let e):Text(e.localizedDescription)case.available(let user):letfields=["ID","First name","Last name","Email"]letvalues=[String(user.id), user.firstName, user.lastName, user.email
]LazyVGrid(columns:[GridItem(),GridItem()]){ForEach(Array(zip(fields, values)), id: \.0){ field, value inText(field)Text(value)}Text("Avatar")AsyncImage(url: user.avatar).aspectRatio(1, contentMode:.fit)}default:Text("Unknown state")}}}

And you define yourself a model

structUser:Codable{varid:Intvaremail:StringvarfirstName:StringvarlastName:Stringvaravatar:URL?}structUserRequest:Encodable{letid:Int}structUserResponse:Decodable{letdata:User}extensionUser:Placeholdable{staticletplaceholder:User=User(
id:0,
email:"empty@example.com",
firstName:"John",
lastName:"Apple",
avatar:nil)}structRemoteUser:Readable{typealiasResource=UsertypealiasReadRequest=UserRequesttypealiasReadResponse=UserResponsenonisolatedstaticfunc endpoint(_ request:ReadRequest)throws(NetworkError)->Endpoint{SampleEndpoint.singleUser(id: request.id)}nonisolatedstaticfunc request(from resource:Resource?)throws(NetworkError)->ReadRequest?{guardlet resource else{throw.missingLocalData }returnUserRequest(id: resource.id)}nonisolatedstaticfunc resource(from response:ReadResponse)throws(NetworkError)->Resource{
response.data
}}

And voila, all is setup. You can update the user directly with a state variable. The library also support all the CRUD operations with protocols Creatable, Readable, Updatable, Deletable and also more like Listable for pagination of lists.

Logging

structSampleLogger:NetworkLogger{func logNetworkEvent(message:Any, level:LogLevel, fileName:String, lineNumber:Int){switch level {case.debug:print("[DEBUG] \(fileName):\(lineNumber) - \(message)")case.info:print("[INFO] \(fileName):\(lineNumber) - \(message)")case.warning:print("[WARNING] \(fileName):\(lineNumber) - \(message)")case.error:print("[ERROR] \(fileName):\(lineNumber) - \(message)")}}}NetworkSession.sampleSession =NetworkSession(
baseUrl: urlProvider,
configuration:.default(logger:SampleLogger()),
logger:SampleLogger())

License

GoodNetworking is released under the MIT license. See LICENSE for details.

About

📡 GoodNetworking is an iOS library written in Swift that simplifies HTTP networking by using GRSession and Encodable/DataRequest extensions. It supports latest Swift and all iOS devices, making it a powerful solution for managing network interactions and data encoding/decoding. The library is easy to install with SPM.

Topics

Resources

Stars

30 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages