Skip to content

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

AsyncNetwork

Foundation, Network, Swift Concurrency 기반으로 구성한 Swift 네트워크 라이브러리입니다.
명시적인 APIRequest 선언, property wrapper 기반 파라미터 구성, NetworkService preset, 재시도 정책, 인터셉터, 네트워크 상태 확인을 한 번에 제공합니다.

Swift Platform License Release

핵심 기능

  • APIRequest 기반의 명시적인 요청 정의
  • @QueryParameter, @PathParameter, @RequestBody, @HeaderField, @CustomHeader
  • NetworkService의 request, requestData, requestRaw
  • RetryPolicy와 RetryConfiguration 기반 재시도 제어
  • RequestInterceptor 기반 요청/응답 가로채기
  • ResponseProcessor, StatusCodeValidator, ResponseDecoder
  • NetworkMonitor.shared 기반 네트워크 연결 상태 확인
  • AsyncNetwork 우산 모듈 + AsyncNetworkCore 코어 product 제공

현재 구조 안내

  • 요청 정의는 매크로 대신 APIRequest 프로토콜과 property wrapper 조합을 사용합니다.
  • 외부 소비자는 AsyncNetwork product를 사용하고, 내부 구현은 AsyncNetworkCore 중심으로 구성됩니다.
  • 매크로 기반 버전에서 올라오는 경우 기존 요청 선언을 일반 APIRequest 타입으로 옮겨야 합니다.

버전별 상세 변경 내역은 CHANGELOG.md를 참고하면 됩니다.

설치

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/Jimmy-Jung/AsyncNetwork.git", from: "1.4.0")
]

일반적인 앱/모듈에서는 AsyncNetwork만 추가하면 됩니다.

.target(
    name: "YourTarget",
    dependencies: [
        .product(name: "AsyncNetwork", package: "AsyncNetwork")
    ]
)

코어 모듈을 직접 선택하고 싶다면 AsyncNetworkCore도 사용할 수 있습니다.

.target(
    name: "YourCoreTarget",
    dependencies: [
        .product(name: "AsyncNetworkCore", package: "AsyncNetwork")
    ]
)

빠른 시작

import AsyncNetwork

struct Post: Codable, Sendable {
    let id: Int
    let title: String
}

struct GetPostsRequest: APIRequest {
    typealias Response = [Post]

    let baseURLString = "https://jsonplaceholder.typicode.com"
    let path = "/posts"
    let method: HTTPMethod = .get

    @QueryParameter var userId: Int?
    @QueryParameter(key: "_limit") var limit: Int?

    init(userId: Int? = nil, limit: Int? = nil) {
        self.userId = userId
        self.limit = limit
    }
}

let service = NetworkService.default(interceptors: [])
let posts = try await service.request(GetPostsRequest(limit: 10))

주요 구성

APIRequest

요청 정의에 필요한 최소 정보만 요구합니다.

public protocol APIRequest: Sendable {
    associatedtype Response: Decodable = EmptyResponse

    var baseURLString: String { get }
    var path: String { get }
    var method: HTTPMethod { get }
    var headers: [String: String]? { get }
    var timeout: TimeInterval { get }
}

APIRequest.asURLRequest()를 통해 최종 URLRequest로 변환할 수 있습니다.

Property Wrappers

  • @QueryParameter: 쿼리 스트링 추가
  • @PathParameter: 경로 플레이스홀더 치환
  • @RequestBody: JSON body 인코딩
  • @HeaderField: 표준 헤더 키 기반 헤더 추가
  • @CustomHeader: 커스텀 헤더 추가
struct UpdatePostRequest: APIRequest {
    typealias Response = Post

    let baseURLString = "https://jsonplaceholder.typicode.com"
    let path = "/posts/{id}"
    let method: HTTPMethod = .put

    @PathParameter var id: Int
    @RequestBody var body: UpdatePostBody
    @HeaderField(key: .contentType) var contentType: String = "application/json"
    @CustomHeader("X-Trace-Id") var traceId: String?
}

NetworkService

let service = NetworkService(
    httpClient: HTTPClient(),
    retryPolicy: RetryPolicy(configuration: .patient),
    interceptors: [ConsoleLoggingInterceptor(minimumLevel: .info)],
    networkMonitor: NetworkMonitor.shared
)

주요 메서드:

  • request(_:): APIRequest.Response로 디코딩
  • request(request:decodeType:): 원하는 Decodable 타입으로 디코딩
  • requestData(_:): 원시 Data 반환
  • requestRaw(_:): HTTPResponse 반환

제공 preset:

  • NetworkService.default(): 일반 JSON API
  • NetworkService.image(): 이미지 다운로드
  • NetworkService.upload(): 파일 업로드
  • NetworkService.download(): 대용량 다운로드
  • NetworkService.realtime(): 빠른 실패가 중요한 실시간 요청
  • NetworkService.offline(): 캐시 우선 오프라인 친화 요청

HTTPClientConfiguration

HTTPClient는 timeout, cache policy, connectivity, 동시 연결 수를 직접 조정할 수 있습니다.

let client = HTTPClient(
    configuration: HTTPClientConfiguration(
        timeoutForRequest: 15,
        timeoutForResource: 60,
        cachePolicy: .reloadIgnoringLocalCacheData,
        waitsForConnectivity: true
    )
)

미리 준비된 설정:

  • HTTPClientConfiguration.image
  • HTTPClientConfiguration.upload
  • HTTPClientConfiguration.download
  • HTTPClientConfiguration.realtime
  • HTTPClientConfiguration.offline

재시도, 인터셉터, 네트워크 모니터링

let retryPolicy = RetryPolicy(configuration: .quick)
let logger = ConsoleLoggingInterceptor(minimumLevel: .debug)
let service = NetworkService(
    retryPolicy: retryPolicy,
    interceptors: [logger],
    networkMonitor: NetworkMonitor.shared
)
  • RetryConfiguration.standard, .quick, .patient
  • URLErrorRetryRule, ServerErrorRetryRule
  • ConsoleLoggingInterceptor
  • NetworkMonitor.shared

Example App

예제 앱은 실제 화면에서 AsyncNetwork 사용 흐름을 확인하기 위한 참고 프로젝트입니다.

  • 경로: Examples/AsyncNetworkExampleApp
  • 화면: Basics, Recipes, Monitor
  • 데모 내용: property wrapper, service preset, interceptor, retry, offline guard

바로 열기:

open Examples/AsyncNetworkExampleApp/AsyncNetworkExampleApp.xcworkspace

프로젝트를 다시 생성해야 하면:

cd Examples/AsyncNetworkExampleApp
tuist install
tuist generate --no-open

로컬 개발

라이브러리 자체를 열어서 작업하려면 루트 workspace를 사용하면 됩니다.

open AsyncNetwork.xcworkspace

기본 요구 환경:

  • Xcode 16.0+
  • Swift 6.0+
  • macOS 14.0+

테스트

swift build
swift test

현재 테스트는 다음 범위를 포함합니다.

  • APIRequest.asURLRequest() 변환
  • NetworkService 요청, 재시도, 오프라인 처리
  • property wrapper 동작
  • HTTPClient, RetryPolicy, ResponseProcessor
  • NetworkMonitor
  • import AsyncNetwork 공개 표면 smoke test

매크로 버전에서 마이그레이션

기존:

@APIRequest(
    response: User.self,
    baseURL: "https://api.example.com",
    path: "/users/{id}",
    method: .get
)
struct GetUserRequest {
    @PathParameter var id: Int
}

이후:

struct GetUserRequest: APIRequest {
    typealias Response = User

    let baseURLString = "https://api.example.com"
    let path = "/users/{id}"
    let method: HTTPMethod = .get

    @PathParameter var id: Int
}

프로젝트 구조

AsyncNetwork/
├── Package.swift
├── AsyncNetwork.xcodeproj
├── AsyncNetwork.xcworkspace
├── Examples/
│   └── AsyncNetworkExampleApp/
├── Projects/
│   └── AsyncNetwork/
│       ├── Sources/
│       └── Tests/
├── Tuist/
└── .github/

License

MIT

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages