Skip to content

hmbz/iOS-Networking-Layer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

iOS Networking Layer — async/await

A production-ready, reusable networking layer for iOS built with Swift async/await.
Drop it into any project and start making API calls in minutes.


Features

  • Generic — works with any Decodable response model
  • async/await — modern Swift concurrency, no callbacks or Combine
  • Interceptors — auto-attach auth tokens, log every request
  • Retry logic — exponential backoff on timeout/connection errors
  • Type-safe errors — every failure has a clear, meaningful message
  • Endpoint protocol — add any new API call by adding one enum case
  • Testable — every class depends on protocols, not concrete types
  • Snake_case — automatic JSON key conversion (user_nameuserName)

Architecture

ViewModel
    ↓
UserService             ← calls NetworkClient, hides implementation details
    ↓
NetworkClient           ← core: builds request, runs interceptors, retries
    ↓
  ┌──────────────┐
  │ Interceptors │  → AuthInterceptor   (adds Bearer token)
  │              │  → LoggingInterceptor (prints request to console)
  └──────────────┘
    ↓
RequestBuilder          ← converts Endpoint → URLRequest
    ↓
URLSession              ← actual network call
    ↓
JSON Response
    ↓
JSONDecoder             ← decodes into your Decodable model
    ↓
ViewModel gets [User] / Post / etc.

How to Use

1. Define an Endpoint

enum UserEndpoint: Endpoint {
    case getUsers
    case getUser(id: Int)
    case createPost(request: CreatePostRequest)

    var path: String {
        switch self {
        case .getUsers:           return "/users"
        case .getUser(let id):    return "/users/\(id)"
        case .createPost:         return "/posts"
        }
    }

    var method: HTTPMethod {
        switch self {
        case .getUsers, .getUser:   return .get
        case .createPost:           return .post
        }
    }

    var body: Encodable? {
        switch self {
        case .createPost(let req):  return req
        default:                    return nil
        }
    }
}

2. Make a Request

let client = NetworkClient()

// GET — fetch a list
let users = try await client.request([User].self, endpoint: UserEndpoint.getUsers)

// GET — fetch by ID
let user = try await client.request(User.self, endpoint: UserEndpoint.getUser(id: 1))

// POST — send a body
let post = try await client.request(Post.self, endpoint: UserEndpoint.createPost(request: req))

3. Handle Errors

do {
    let users = try await service.fetchUsers()
} catch let error as NetworkError {
    switch error {
    case .noInternetConnection:  showAlert("No internet")
    case .unauthorized:          navigateToLogin()
    case .serverError(let code): print("Server error: \(code)")
    default:                     showAlert(error.localizedDescription)
    }
}

Project Structure

NetworkingLayer/
├── Core/
│   ├── HTTPMethod.swift          ← GET, POST, PUT, PATCH, DELETE
│   ├── NetworkError.swift        ← All error cases with descriptions
│   ├── Endpoint.swift            ← Protocol every endpoint conforms to
│   ├── RequestBuilder.swift      ← Endpoint → URLRequest conversion
│   └── NetworkClient.swift       ← Main client with retry + interceptors
├── Interceptors/
│   ├── Interceptor.swift         ← Base protocol
│   ├── AuthInterceptor.swift     ← Attaches Bearer token automatically
│   └── LoggingInterceptor.swift  ← Logs all requests in DEBUG builds
├── Endpoints/
│   └── UserEndpoint.swift        ← Real example: GET/POST/PUT/DELETE
├── Models/
│   └── User.swift                ← Decodable response models
└── Demo/
    ├── UserService.swift          ← Service layer on top of NetworkClient
    └── UsersViewModel.swift       ← ViewModel consuming the service

Error Types

Error When it happens
invalidURL URL could not be constructed
noInternetConnection Device is offline
requestTimeout Request exceeded time limit
unauthorized 401 — token expired
forbidden 403 — no permission
notFound 404 — resource missing
serverError(statusCode) 500+ — server problem
decodingFailed JSON didn't match the model

Requirements

Version
iOS 16.0+
Swift 5.9+
Xcode 15.0+

Author

Bilal Zafar — iOS Developer
GitHubLinkedIn

About

Production-ready iOS Networking Layer — async/await, interceptors, retry logic, type-safe errors, fully testable

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages