Skip to content

Weixi779/Moira

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Moira

Platform Swift SPM Support License

English | 简体中文

Moira is a lightweight networking layer built for Swift Concurrency. It keeps request construction explicit and composable without forcing application architecture choices.

Why Moira

Moira carries a personal tribute to Moya. I learned a lot from its API modeling ideas, but over time the maintenance burden, concurrency-era gaps, and growing integration friction made it hard to continue relying on it in modern projects.

Moira is the practical answer to that transition:

  • Keep the networking layer thin and replaceable
  • Preserve semantic API description and composability
  • Embrace async/await-native request flow and clear lifecycle extension points

Design background: 「iOS」网络层工程范式迁移

Highlights

  • Request description: APIRequest + RequestPayload
  • Predictable build rules: RequestBuilder
  • Pluggable lifecycle: Transform / Observer / Retry / ShortCircuit
  • Default decoding via ResponseDecoder
  • Upload/download progress via RequestTask<APIResponse>.progress

Quick Example

import Moira

enum UserAPI: APIRequest {
    case profile(id: String)
    case search(query: String)
    case updateProfile(id: String, payload: UpdateProfile)

    var path: String {
        switch self {
        case .profile(let id):
            return "/users/\(id)"
        case .search:
            return "/users/search"
        case .updateProfile(let id, _):
            return "/users/\(id)"
        }
    }

    var method: RequestMethod {
        switch self {
        case .profile, .search:
            return .get
        case .updateProfile:
            return .patch
        }
    }
    var payload: RequestPayload {
        switch self {
        case .profile:
            return RequestPayload()
        case .search(let query):
            return RequestPayload().appendingQueryItem(URLQueryItem(name: "q", value: query))
        case .updateProfile(_, let body):
            return RequestPayload().withJSON(body)
        }
    }
}

struct UpdateProfile: Encodable, Sendable {
    let name: String
}

let baseURL = URL(string: "https://api.example.com")!
let provider = APIProvider(
    client: AlamofireClient(),
    builder: RequestBuilder(baseURL: baseURL)
)

let user: User = try await provider.request(UserAPI.profile(id: "123"))

Payload Examples

let queryPayload = RequestPayload()
    .appendingQueryItems([URLQueryItem(name: "page", value: "1")])

let formPayload = RequestPayload()
    .withURLEncodedForm([URLQueryItem(name: "q", value: "swift")])

let dataPayload = RequestPayload()
    .withData(Data("raw".utf8))

Raw Response

let response = try await provider.requestResponse(UserAPI.profile(id: "123"))
print(response.statusCode)
print(response.data)

Upload/Download with Progress

let request = UploadAPI.data(Data("payload".utf8))
let task = try await provider.requestTask(request)

if let progress = task.progress {
    Task {
        for await update in progress {
            print(update.completedBytes)
        }
    }
}

let response = try await task.response()
print(response.statusCode)

Docs

Moira ships with built-in documentation for both Chinese and English readers.

Index:

  • English docs index: Docs/index.md
  • Chinese docs index: Docs/zh/index.md

Core docs:

  • Getting Started: Docs/Getting-Started.md / Docs/zh/Getting-Started.md
  • Core Types: Docs/Core-Types.md / Docs/zh/Core-Types.md
  • Plugins: Docs/Plugins.md / Docs/zh/Plugins.md
  • Request Building: Docs/Request-Building.md / Docs/zh/Request-Building.md
  • Clients: Docs/Clients.md / Docs/zh/Clients.md
  • Architecture: Docs/Architecture.md / Docs/zh/Architecture.md

Build & Test

swift build
swift test

About

Lightweight Swift Concurrency networking layer with a composable request-building pipeline.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages