Easily fetch remote data with Property Wrapper

SwiftUIQuery

Under heavy development!

A SwiftUI way to fetch remote data – inspired by CoreData @FetchRequest property wrapper

Platforms

  [.iOS(.v14), .macOS(.v11), .macCatalyst(.v14), .tvOS(.v14), .watchOS(.v7)]

Usage

import SwiftUI
import SwiftUIQuery

struct ExampleView: View {
  @Query(request: URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos")!))
  private var result: QueryResult<[Todo]>

  var body: some View {
    List {
      if let todos = result.data {
        ForEach(todos) { todo in
          Text(todo.title)
        }
      }
    }
    .refreshable {
      // Integrate with .refreshable modifier easily
      await result.refetch()
    }
  }
}

Stub/Mock Query for SwiftUI Preview

In Preview Content group, create new file e.g. PreviewQueryClient and confirm to a QueryClient protocol:

import SwiftUIQuery

final class PreviewQueryClient: QueryClient {
  // QueryClient required only one method
  func query<Result>(_ request: URLRequest) async throws -> Result where Result: Decodable {
    return [
      Todo(userId: 1, id: 1, title: "First todo", completed: false),
      Todo(userId: 1, id: 2, title: "Second todo", completed: true)
    ]
  }
}

In preview section of your view, modify queryClient EnvironmentKey:

struct ExampleView_Previews: PreviewProvider {
  static var previews: some View {
    ExampleView()
      .environment(\.queryClient, PreviewQueryClient())
}

…and now previews don’t call network!

Custom QueryClient

You can change default implementation for QueryClient for whole View Hierarchy:

import SwiftUI

@main
struct SwiftUIQueryExampleApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
        .environment(\.queryClient, CustomQueryClient())
    }
  }
}

… or a specific view:

struct ExampleView: View {
  var body: some View {
    TodosView()
      .environment(\.queryClient, TodosQueryClient())
  }
}

Using QueryClient in View

You can access to Environment QueryClient and process URLRequest outside of @Query property e.g. mutations

import SwiftUI
import SwiftUIQuery

struct ExampleView: View {
  @Environment(\.queryClient) private var queryClient

  @Query(request: URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos")!))
  private var result: QueryResult<[Todo]>

  @State private var successed = false

  var body: some View {
    List {
      // ...
    }.toolbar {
      ToolbarItem {
        Button("New Todo", action: createTodo)
      }
    }
  }

  private func createTodo() {
    let request: URLRequest = {
      // Create and configure request to send mutation e.g. change httpMethod etc.
    }()

    Task {
      do {
        try await queryClient.query(request)
        successed = true
        
        // You can refresh query here:
        // await result.refetch()
        
      } catch {
        // ... handle request error
      }
    }
  }
}

Installation

Swift Package Manager

To integrate using Apple’s Swift Package Manager, add the following as a dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/kubens/SwiftUIQuery.git", from: "0.1.0")
]

Or navigate to your Xcode project then select Swift Packages, click the “+” icon and search for SwiftUIQuery.

Todo

  • Tests!
  • Better documentation
  • Mutation property wrapper

GitHub

View Github