بنقرة واحدة
networking-guide
HTTP networking layer reference — TargetType, HTTPClient, services, error handling.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
HTTP networking layer reference — TargetType, HTTPClient, services, error handling.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Implement UI from Figma designs via MCP with a closed-loop pixel-parity check. Extract exact values (never eyeball), translate to SwiftUI, then render→diff→fix against the Figma export until it matches. Use when implementing or auditing UI against Figma.
Fetch CodeRabbit review comments from batch PRs and auto-fix the issues. Pushes fixes and re-requests review.
Run multiple tasks overnight as parallel PRs. Produces a PRD per task, selects skills/rules, spawns isolated agents, and reports PR links.
Run multiple tasks overnight as parallel PRs. Produces a PRD per task, selects skills/rules, spawns isolated agents, and reports PR links.
Run SwiftLint and xcodebuild to verify the project compiles cleanly.
Create a GitHub issue for vultisig-ios using bug/feature templates.
| name | networking-guide |
| description | HTTP networking layer reference — TargetType, HTTPClient, services, error handling. |
| user-invocable | false |
All networking uses a custom HTTP client built on URLSession with async/await.
Do not call URLSession or construct URLRequest outside Core/Networking/. SwiftLint custom rules (no_raw_urlsession, no_raw_urlrequest) flag violations as warnings today and will flip to errors once the Architecture 3 HTTPClient migration completes. If you see those warnings in a file you're editing, define a TargetType and route the call through HTTPClient.
Key files (all under VultisigApp/VultisigApp/Core/Networking/):
| File | Purpose |
|---|---|
TargetType.swift | Endpoint definition protocol + HTTPTask + ParameterEncoding + ValidationType |
HTTPClient.swift | URLSession wrapper implementation |
HTTPClientProtocol.swift | Protocol for dependency injection |
HTTPError.swift | Error types + HTTPResponse struct |
HTTPMethod.swift | HTTP methods enum |
public protocol TargetType {
var baseURL: URL { get }
var path: String { get }
var method: HTTPMethod { get }
var task: HTTPTask { get }
var headers: [String: String]? { get }
var validationType: ValidationType { get } // default: .successCodes
var timeoutInterval: TimeInterval { get } // default: 60.0
}
// Default header: ["Content-Type": "application/json"]
public enum HTTPTask {
case requestPlain // No parameters
case requestParameters([String: Any], ParameterEncoding) // Dictionary params
case requestData(Data) // Raw data body
case requestCompositeData(bodyData: Data, urlParameters: [String: Any]) // Body + URL params
case requestCodable(Encodable, ParameterEncoding) // Codable object
}
public enum ParameterEncoding {
case urlEncoding // Query parameters (GET requests)
case jsonEncoding // JSON body (POST/PUT/PATCH)
case formEncoding // Form data (application/x-www-form-urlencoded)
}
public enum ValidationType {
case noValidation
case successCodes // 200-299 (default)
case customCodes([Int])
}
public enum HTTPMethod: String, CaseIterable {
case get = "GET"
case post = "POST"
case put = "PUT"
case patch = "PATCH"
case delete = "DELETE"
case head = "HEAD"
case options = "OPTIONS"
}
protocol HTTPClientProtocol {
func request(_ target: TargetType) async throws -> HTTPResponse<Data>
func request<T: Decodable>(_ target: TargetType, responseType: T.Type) async throws -> HTTPResponse<T>
func requestEmpty(_ target: TargetType) async throws -> HTTPResponse<EmptyResponse>
}
enum HTTPError: Error, LocalizedError {
case invalidURL
case noData
case invalidResponse
case statusCode(Int, Data?)
case encodingFailed
case decodingFailed(Error)
case networkError(Error)
case timeout
case invalidSSLCertificate
}
struct HTTPResponse<T> {
let data: T
let response: HTTPURLResponse
}
Define endpoints as a TargetType enum:
// Services/[Feature]/[Feature]API.swift
enum MyFeatureAPI: TargetType {
case getData(id: String)
case postData(payload: MyPayload)
var baseURL: URL {
URL(string: "https://api.example.com")!
}
var path: String {
switch self {
case .getData(let id): return "/data/\(id)"
case .postData: return "/data"
}
}
var method: HTTPMethod {
switch self {
case .getData: return .get
case .postData: return .post
}
}
var task: HTTPTask {
switch self {
case .getData: return .requestPlain
case .postData(let payload): return .requestCodable(payload, .jsonEncoding)
}
}
var headers: [String: String]? {
["X-Client-ID": "vultisig"]
}
}
Services use constructor injection:
// Services/[Feature]/[Feature]Service.swift
struct MyFeatureService {
let httpClient: HTTPClientProtocol
init(httpClient: HTTPClientProtocol = HTTPClient()) {
self.httpClient = httpClient
}
func getData(id: String) async throws -> MyModel {
let response = try await httpClient.request(
MyFeatureAPI.getData(id: id),
responseType: MyModel.self
)
return response.data
}
}
Use domain-specific error enums:
enum MyFeatureError: Error, LocalizedError {
case notFound
case invalidData
var errorDescription: String? {
switch self {
case .notFound: return "Resource not found"
case .invalidData: return "Invalid data received"
}
}
}
File: Blockchain/THORChain/API/TargetType/THORChainAPI.swift
enum THORChainAPI: TargetType {
case getThornameDetails(name: String)
case getPools
case getPoolAsset(asset: String)
case getLastBlock
case getNetworkFees
case getConstants
// ...
var baseURL: URL {
switch self {
case .getThornameDetails, .getPools, .getPoolAsset, .getLastBlock, .getNetworkFees, .getConstants:
return URL(string: "https://thornode.ninerealms.com/thorchain")!
case .getThornameLookup, .getAddressLookup, .getHealth, .getNetworkInfo:
return URL(string: "https://midgard.ninerealms.com")!
}
}
var method: HTTPMethod { .get }
var task: HTTPTask { .requestPlain }
}
Service: Blockchain/THORChain/API/THORChainAPIService.swift
struct THORChainAPIService {
let httpClient: HTTPClientProtocol
let cache = THORChainAPICache()
init(httpClient: HTTPClientProtocol = HTTPClient()) {
self.httpClient = httpClient
}
func getPools() async throws -> [THORChainPoolResponse] {
if let cached = await cache.getCachedPools() { return cached }
let response = try await httpClient.request(
THORChainAPI.getPools,
responseType: [THORChainPoolResponse].self
)
await cache.cachePools(response.data)
return response.data
}
}
File: Blockchain/Tron/Service/TronAPI.swift
enum TronAPI: TargetType {
case getNowBlock
case getAccount(address: String)
case broadcastTransaction(jsonString: String)
case triggerConstantContract(ownerAddress: String, contractAddress: String,
functionSelector: String, parameter: String)
var method: HTTPMethod {
switch self {
case .getNowBlock, .getChainParameters: return .get
case .getAccount, .broadcastTransaction, .triggerConstantContract: return .post
}
}
var task: HTTPTask {
switch self {
case .getNowBlock: return .requestPlain
case .getAccount(let address):
return .requestParameters(["address": address, "visible": true], .jsonEncoding)
case .broadcastTransaction(let jsonString):
guard let data = jsonString.data(using: .utf8) else { return .requestPlain }
return .requestData(data)
case .triggerConstantContract(let owner, let contract, let selector, let param):
return .requestParameters([
"owner_address": owner, "contract_address": contract,
"function_selector": selector, "parameter": param, "visible": true
], .jsonEncoding)
}
}
}
actor MyCache {
private var cachedData: [Item]?
private var lastFetch: Date?
private let cacheDuration: TimeInterval = 300
func getCachedData() -> [Item]? {
guard let data = cachedData, let lastFetch,
Date().timeIntervalSince(lastFetch) < cacheDuration
else { return nil }
return data
}
func cacheData(_ data: [Item]) {
cachedData = data
lastFetch = Date()
}
}
Reference: Blockchain/THORChain/API/THORChainAPICache.swift