I have the following code in my application:
struct HomeView: View {
// other variables omitted for brevity
// Profile is a struct which implements Codable and Identifiable
@State private var friends: TypedResource<[Profile]> = ApiService.main.service.resource("/friends").typed()
private func buildFriendsList() -> some View {
VStack(alignment: .leading, spacing: 16.0) {
Text("your nearby friends")
.font(Font.DesignSystem.displaySmBold)
ResourceView(friends, statusDisplay: .standard) { friends in
List {
ForEach(friends) { item in
HStack {
VStack(alignment: .leading) {
Text(item.fullName)
.font(Font.DesignSystem.textMdSemibold)
.listRowBackground(Color.bgSecondary)
}
}
.padding(.vertical, 8.0)
}
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets())
}
.overlay(alignment: .top) {
if friends.isEmpty {
ContentUnavailableView {
Label("no friends", systemImage: "person.fill.questionmark")
} description: {
Text("add your friends on rdvz to invite them on adventures")
}
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
}
}
.padding(.horizontal, 24.0)
.padding(.vertical, 24.0)
}
}
And my ApiService.swift looks like this:
import Foundation
import Siesta
import Siesta_Alamofire
import SiestaExt
import Alamofire
import Combine
import AlamofireNetworkActivityLogger
@MainActor
class ApiService {
var service: Service
var authToken: String? {
didSet {
DispatchQueue.main.schedule { [weak self] in
self?.service.invalidateConfiguration()
self?.service.wipeResources()
}
}
}
init() {
#if DEBUG
SiestaLog.Category.enabled = .all
NetworkActivityLogger.shared.level = .debug
NetworkActivityLogger.shared.startLogging()
#endif
let reqManager = Session()
service = Service(
baseURL: "http://localhost:4321/api",
standardTransformers: [.json, .text],
networking: reqManager
)
service.configure("**") { [weak self] in
$0.headers["user-agent"] = "rdvz/ios"
$0.headers["accept"] = "application/json"
if let authToken = self?.authToken {
$0.headers["authorization"] = "Bearer \(authToken)"
}
}
Task { [weak self] in
for await (event, session) in await supabaseClient.auth.authStateChanges {
self?.authToken = session?.accessToken
}
}
}
static let main = ApiService()
}
When Supabase emits an event to point out that the user has logged in, this happens at the same time as the initial request to the backend for the friends list. The initial request is cancelled, but then the application doesn't send another request to get the data, it just does nothing and shows no data.
In the Xcode preview, where I am not logged in with Supabase, I can see the request hitting my localhost server, but in the Simulator, where I am logged in, no request ever reaches my server. I also tried making HomeView.friends a computed property, as well as inlining the call to .resource() into my view builder; neither of these approaches worked.
The logs from my app look like this:
Siesta:configuration │ Added config 0 [Siesta standard JSON parsing]
Siesta:configuration │ Added config 1 [Siesta standard text parsing]
Siesta:configuration │ URL pattern ** compiles to regex ^http:\/\/localhost:4321\/api\/[^?]*($|\?)
Siesta:configuration │ Added config 2 [**]
Siesta:configuration │ Computing configuration for GET Resource(…/friends)[]
Siesta:configuration │ ├╴Applying config 0 [Siesta standard JSON parsing]
Siesta:configuration │ ├╴Applying config 1 [Siesta standard text parsing]
Siesta:configuration │ ├╴Applying config 2 [**]
Siesta:configuration │ └╴Resulting configuration
Siesta:configuration │ expirationTime: 30.0 sec
Siesta:configuration │ retryTime: 1.0 sec
Siesta:configuration │ progressReportingInterval: 0.05 sec
Siesta:configuration │ headers (2)
Siesta:configuration │ user-agent: rdvz/ios
Siesta:configuration │ accept: application/json
Siesta:configuration │ requestDecorators: 0
Siesta:configuration │ pipeline
Siesta:configuration │ ║ rawData stage (no transformers)
Siesta:configuration │ ║ decoding stage (no transformers)
Siesta:configuration │ ║ parsing stage
Siesta:configuration │ ╟ ⟨*/json */*+json⟩ Data → JSONConvertible [transformErrors: true]
Siesta:configuration │ ╟ ⟨text/*⟩ Data → String [transformErrors: true]
Siesta:configuration │ ║ model stage (no transformers)
Siesta:configuration │ ║ cleanup stage (no transformers)
Siesta:network │ Cache request for Resource(…/friends)[]
Siesta:staleness │ Resource(…/friends)[] is not up to date: no error | no data
Siesta:network │ Chain[Request:6000009f6080(Cache request for Resource(…/friends)[])]
Siesta:networkDetails │ Cache request for Resource(…/friends)[] already started
Siesta:staleness │ Resource(…/friends)[L] is not up to date: no error | no data
Siesta:staleness │ Resource(…/friends)[] is not up to date: no error | no data
Siesta:networkDetails │ Request:
Siesta:networkDetails │ headers (2)
Siesta:networkDetails │ User-Agent: rdvz/ios
Siesta:networkDetails │ Accept: application/json
Siesta:network │ GET http://localhost:4321/api/friends
Siesta:observers │ Resource(…/friends)[L] sending requested event to 1 observer
Siesta:observers │ ↳ requested → ClosureObserver(ObservableResource.swift:15)
Siesta:staleness │ Resource(…/friends)[L] is not up to date: no error | no data
Siesta:networkDetails │ GET http://localhost:4321/api/friends already started
Siesta:configuration │ Configurations need to be recomputed
Siesta:stateChanges │ Resource(…/friends)[L] wiped
Siesta:network │ Cancelled GET http://localhost:4321/api/friends
Siesta:network │ cancel() called but request already completed: GET http://localhost:4321/api/friends
Siesta:observers │ Resource(…/friends)[L] sending newData(wipe) event to 1 observer
Siesta:observers │ ↳ newData(wipe) → ClosureObserver(ObservableResource.swift:15)
Siesta:configuration │ Computing configuration for GET Resource(…/friends)[L]
Siesta:configuration │ ├╴Applying config 0 [Siesta standard JSON parsing]
Siesta:configuration │ ├╴Applying config 1 [Siesta standard text parsing]
Siesta:configuration │ ├╴Applying config 2 [**]
Siesta:configuration │ └╴Resulting configuration
Siesta:configuration │ expirationTime: 30.0 sec
Siesta:configuration │ retryTime: 1.0 sec
Siesta:configuration │ progressReportingInterval: 0.05 sec
Siesta:configuration │ headers (3)
Siesta:configuration │ authorization: Bearer <JWT goes here, I removed it>
Siesta:configuration │ user-agent: rdvz/ios
Siesta:configuration │ accept: application/json
Siesta:configuration │ requestDecorators: 0
Siesta:configuration │ pipeline
Siesta:configuration │ ║ rawData stage (no transformers)
Siesta:configuration │ ║ decoding stage (no transformers)
Siesta:configuration │ ║ parsing stage
Siesta:configuration │ ╟ ⟨*/json */*+json⟩ Data → JSONConvertible [transformErrors: true]
Siesta:configuration │ ╟ ⟨text/*⟩ Data → String [transformErrors: true]
Siesta:configuration │ ║ model stage (no transformers)
Siesta:configuration │ ║ cleanup stage (no transformers)
Siesta:staleness │ Resource(…/friends)[L] is not up to date: no error | no data
Siesta:network │ Cache request for Resource(…/friends)[L]
---------------------
GET 'http://localhost:4321/api/friends':
cURL:
$ curl -v \
-X GET \
-H "Accept-Language: en-US;q=1.0" \
-H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
-H "User-Agent: rdvz/ios" \
-H "Accept: application/json" \
"http://localhost:4321/api/friends"
Siesta:observers │ Resource(…/friends)[] sending requestCancelled event to 1 observer
Siesta:observers │ ↳ requestCancelled → ClosureObserver(ObservableResource.swift:15)
Siesta:staleness │ Resource(…/friends)[] is not up to date: no error | no data
---------------------
[Error] GET 'http://localhost:4321/api/friends' [0.0242 s]:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=http://localhost:4321/api/friends, NSLocalizedDescription=cancelled, NSErrorFailingURLKey=http://localhost:4321/api/friends}
Siesta:network │ Response: explicitlyCancelled ← GET http://localhost:4321/api/friends
Siesta:networkDetails │ Raw response headers: nil
Siesta:networkDetails │ Raw response body: 0 bytes
Siesta:networkDetails │ Received response, but request was already cancelled: GET http://localhost:4321/api/friends
Siesta:networkDetails │ New response: RequestError(userMessage: "Request explicitly cancelled.", httpStatusCode: nil, entity: nil, cause: Optional(Alamofire.AFError.explicitlyCancelled), timestamp: 732752061.707411)
I have the following code in my application:
And my
ApiService.swiftlooks like this:When Supabase emits an event to point out that the user has logged in, this happens at the same time as the initial request to the backend for the friends list. The initial request is cancelled, but then the application doesn't send another request to get the data, it just does nothing and shows no data.
In the Xcode preview, where I am not logged in with Supabase, I can see the request hitting my
localhostserver, but in the Simulator, where I am logged in, no request ever reaches my server. I also tried makingHomeView.friendsa computed property, as well as inlining the call to.resource()into my view builder; neither of these approaches worked.The logs from my app look like this: