How do you add listener to your SyncSubscriptions? The code from the documentation doesn’t seem to work:
let results = realm.objects(Person.self).filter("age > 18")
let subscription = results.subscribe()
let subscriptionToken = subscription.observe(\.state) { state in
switch state {
case .creating:
print("creating")
case .pending:
print("pending")
case .complete:
print("complete")
case .invalidated:
print("invalidated")
case .error(let err):
print("err")
}
This code does not execute any of those cases. Am i missing something? TIA
Related
Has anyone succeeded in creating an AsyncStream and setting its onTermination handler? I can't do it. The following is copied and pasted directly from the proposal (https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md), except I got rid of the warnings by modernizing detach into Task.detached:
let t = Task.detached {
func make123Stream() -> AsyncStream<Int> {
AsyncStream { continuation in
continuation.onTermination = { termination in
switch termination {
case .finished:
print("Regular finish")
case .cancelled:
print("Cancellation")
}
}
Task.detached {
for n in 1...3 {
continuation.yield(n)
sleep(2)
}
continuation.finish()
}
}
}
for await n in make123Stream() {
print("for-in: \(n)")
}
print("After")
}
sleep(3)
t.cancel()
Looks great, but it doesn't compile, and I can't find a way to make it compile. The error message on the onTermination setter reads:
Converting non-concurrent function value to
'#Sendable (AsyncStream<Int>.Continuation.Termination) -> Void'
may introduce data races
I don't know what the compiler is asking me to do. Has anyone worked this out, and what's the solution?
(I've filed a bug on this.)
Update:
You can work around this bug by adding #Sendable as the first thing inside the closure (before the capture list, parameters, and the in keyword), like:
continuation.onTermination = { #Sendable termination in
switch termination {
case .finished:
print("Regular finish")
case .cancelled:
print("Cancellation")
#unknown default:
break
}
}
Original answer:
Yeah I'm guessing it's a bug, because I was able to get it to compile by adding:
as (#Sendable (AsyncStream<Int>.Continuation.Termination) -> Void)
after the closure, like:
continuation.onTermination = { termination in
switch termination {
case .finished:
print("Regular finish")
case .cancelled:
print("Cancellation")
#unknown default:
break
}
} as (#Sendable (AsyncStream<Int>.Continuation.Termination) -> Void)
(I also added the #unknown default case to silence a new warning that appeared.)
good morning community,
I have a very good question that I try to implement in a project but I am something new to combine
I want to do a function to check some permissions, but I want to return an AnyPublisher with a tuple inside, could someone help me who has already done it or know how to do it?
I put my code below.
func returnedPermisionReminderAuthorizationStatus(reminderPermission:EKAuthorizationStatus,calendarPermission:EKAuthorizationStatus) -> AnyPublisher<(EKAuthorizationStatus,EKAuthorizationStatus),Never>{
var reminderPermissionToPass:EKAuthorizationStatus = .notDetermined
var calendarPermissionToPass:EKAuthorizationStatus = .notDetermined
switch (reminderPermission){
case .notDetermined:
return Just(reminderPermissionToPass).eraseToAnyPublisher()
case .restricted:
reminderPermissionToPass = .restricted
return Just(reminderPermissionToPass).eraseToAnyPublisher()
case .denied:
reminderPermissionToPass = .denied
return Just(reminderPermissionToPass).eraseToAnyPublisher()
case .authorized:
reminderPermissionToPass = .authorized
return Just(reminderPermissionToPass).eraseToAnyPublisher()
#unknown default:
reminderPermissionToPass = .notDetermined
return Just(reminderPermissionToPass).eraseToAnyPublisher()
}
}
Is it possible to send a tuple in a just?
You can send a tuple from Just like this:
func returnedPermisionReminderAuthorizationStatus(
reminderPermission: EKAuthorizationStatus,
calendarPermission: EKAuthorizationStatus
) -> AnyPublisher<(EKAuthorizationStatus,EKAuthorizationStatus),Never>{
Just((reminderPermission,calendarPermission))
.eraseToAnyPublisher()
}
What about a PassthroughSubject ? If yes then try the following code :
let someResponse = PassthroughSubject<(Bool,String), Never>()
and then to receive
.onReceive(someResponse, perform: { (boolValue,StringValue) in
})
I started using Result as a return type and I mostly like it but when I have nothing to return for success then I am at a loss about what to do in that case statement. Any hints?
All that I could think of was let _ = 0
func createAppDirectory(_ searchPath: FileManager.SearchPathDirectory) -> Result<Void,Error>
...
switch createAppDirectory(searchPath) {
case .success(_): let _ = 0
case .failure(let error): return .failure(error)
}
I am beginning to think that maybe Result isn't a good fit when the success type is Void.
BTW createAppDirectory just creates Library/Application Support/<Bundle ID>. There is no value to return if it succeeds.
Use a break statement:
switch createAppDirectory(searchPath) {
case .success:
break
case .failure(let error): return .failure(error)
}
EDIT:
As Mojtaba pointed out, if you're not going to use the associated value for a particular case of your enum you can simply skip it. I've edited my answer above to remove the (_) from the .success case
Just ignore it:
case .success: break
Also if you want absolutely no overwork when it isn't failure case, gaurd it at the very beginning of the scope:
guard case .failure(let error) = createAppDirectory(searchPath) else { return <#Value#> }
If only the error is significant Result is inappropriate.
A better pattern is to throw the error and return nothing
func createAppDirectory(_ searchPath: FileManager.SearchPathDirectory) throws
...
do {
try createAppDirectory(searchPath)
} catch { print(error)}
Return simple void result,
switch createAppDirectory(searchPath) {
case .success: return .success(())
case .failure(let error): return .failure(error)
}
I have this code in a ServiceClient. It handles service-level calls, like signIn(user, password, completion), listObjects(completion), addObject(objectID, content, completion), getObject(id, completion) etc. It contains (but doesn't subclass) an APIClient, which performs only basic HTTPS services like perform(request, completion).
I don't really want the controller that sits above this to deal with 404s as success, which means trapping the error in ServiceClient. So the idea is APIClient deals with networking errors whereas ServiceClient deals with unexpected HTTP results.
So I end up with this in ServiceClient, where errors like invalidURL are converted from an APIClient enum to a ServiceClient enum:
apiClient.perform(request) {result in
switch result {
case .success(let data):
guard data.statusCode == 200 else {
completion(.failure(.badResponse))
return
}
completion(.success(data))
case .failure(let error):
switch error {
case .invalidURL:
completion(.failure(.invalidURL))
case .requestFailed:
completion(.failure(.requestFailed))
case .decodingFailure:
completion(.failure(.decodingFailure))
}
}
}
I think in this case I'll just make APIClient handle invalid HTTP status codes, but what's the more general solution to this? At some point I'll want different error codes for different service clients, at which point this becomes a problem again.
I suggest using Int type enumeration for both ServiceClient and APIClient.
As I understood this is your custom enumerations.
So, assuming you have ServiceClientError and APIClientError you can implement them using this way:
enum ServiceClientError: Int {
case invalidURL, requestFailed, decodingFailure
}
enum APIClientError: Int {
case invalidURL, requestFailed, decodingFailure
}
You can create your custom conversion method:
extension ServiceClientError {
static func create(from apiClientError: APIClientError) -> ServiceClientError {
return ServiceClientError(rawValue: apiClientError.rawValue)
}
}
Wanted function:
apiClient.perform(request) {result in
switch result {
case .success(let data):
guard data.statusCode == 200 else {
completion(.failure(.badResponse))
return
}
completion(.success(data))
case .failure(let error):
guard let serviceClientError = ServiceClientError.create(from: error) else {
/// Handle incorrect behavior
fatalError("Wrong enumeration mapping")
return
}
completion(.failure(serviceClientError))
}
}
I have created a Result enum to propagate the result of a REST API call.
enum Result {
case success([AnyObject])
case failure(APIError)
}
I also have a Error enum to handler errors
enum APIError: Error {
case requestFailed(String)
case invalidData(String)
case invalidQuery(String)
case invalidURL(String)
}
I send the error in a completion closure like
completion(.failure(.invalidURL("Invalid URL")))
How can I access this string in a if case scenario?
I am trying to do something like
if case .failure(let res) = result /*, case res.invalidQuery(let invalid) */ {
print(res)
}
How can I achieve this?
If what you're trying to do is having two cases on the same line, you can do it like so:
if case .failure(let error) = result, case .invalidQuery(let message) = error {
print(message)
}