Sqwery is a Swift library that provides a powerful and flexible way to manage asynchronous data fetching and state management in SwiftUI applications. It offers a clean and declarative API for handling queries and mutations, inspired by TanStack Query.
- Declarative query and mutation definitions
- Automatic caching and invalidation
- Retry logic with customizable delays
- SwiftUI integration with property wrappers
- Support for HTTP requests with Alamofire
First, define your query by conforming to the QueryKey
protocol:
struct UserQuery: QueryKey {
let userId: Int
var resultLifetime: Duration { .seconds(60) }
var retryDelay: Duration { .seconds(5) }
var retryLimit: Int { 3 }
func run() async throws -> User {
// Fetch user data from an API
let url = URL(string: "https://api.example.com/users/\(userId)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
}
Use the @Query
property wrapper to integrate your query with SwiftUI:
struct UserView: View {
@Query(UserQuery(userId: 1), queryClient: queryClient)
var userQuery
var body: some View {
switch userQuery {
case .success(let user):
Text(user.name)
case .error(let error):
Text("Error: \(error.localizedDescription)")
case .pending:
ProgressView()
case .idle:
Text("Idle")
}
}
}
Define a mutation by conforming to the MutationKey
protocol:
struct UpdateUserMutation: MutationKey {
typealias Parameter = UpdateUserParams
typealias Result = User
var retryDelay: Duration { .seconds(5) }
var retryLimit: Int { 3 }
func run(parameter: UpdateUserParams) async throws -> User {
// Update user data via API
let url = URL(string: "https://api.example.com/users/\(parameter.userId)")!
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.httpBody = try JSONEncoder().encode(parameter)
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(User.self, from: data)
}
}
Use the @Mutation
property wrapper to integrate your mutation with SwiftUI:
struct UpdateUserView: View {
@Mutation(UpdateUserMutation(), mutationClient: mutationClient)
var updateUserMutation
@State private var name = ""
var body: some View {
VStack {
TextField("Name", text: $name)
Button("Update User") {
let params = UpdateUserParams(userId: 1, name: name)
$updateUserMutation.mutate(parameter: params)
}
switch updateUserMutation {
case .success(let user):
Text("Updated: \(user.name)")
case .error(let error):
Text("Error: \(error.localizedDescription)")
case .pending:
ProgressView()
case .idle:
EmptyView()
}
}
}
}
Sqwery provides convenience protocols for HTTP queries and mutations using Alamofire:
struct FetchUserQuery: HttpQueryKey {
let userId: Int
var url: String { "https://api.example.com/users/\(userId)" }
var method: HTTPMethod { .get }
typealias Result = User
}
struct CreateUserMutation: HttpJsonMutationKey {
typealias Body = CreateUserParams
typealias Result = User
var url: String { "https://api.example.com/users" }
var method: HTTPMethod { .post }
func bodyData(for parameter: CreateUserParams) throws -> CreateUserParams {
parameter
}
}
You can invalidate queries to force a refresh:
await queryClient.invalidate(key: UserQuery(userId: 1))
Sqwery uses an internal cache to store query results. You can customize caching behavior by modifying the resultLifetime
property of your queries.