I have the following code:
Here is where I init the database ref and the handle
private let gameResult = Database.database().reference().child("GameResults")
//
private var gameStatusRefHandle: DatabaseHandle!
This is the function where I set the handle
func getPlayerStatus(gameId: String, completion: #escaping (_ success: Bool) -> Void) {
self.gameStatusRefHandle = self.gameResult.child(gameId).child((Auth.auth().currentUser?.uid)!).observe(DataEventType.value) { (snapshot) in
completion(snapshot.value as! Bool)
}
}
and this is where I remove it:
func removeObserverHandle(gameId: String) {
self.gameResult.child(gameId).child((Auth.auth().currentUser?.uid)!).removeObserver(withHandle: self.gameStatusRefHandle)
}
The above code is all in a class:
class GameStore: ObservableObject {
private let gameResult = Database.database().reference().child("GameResults")
//
private var gameStatusRefHandle: DatabaseHandle!
func getPlayerStatus(gameId: String, completion: #escaping (_ success: Bool) -> Void) {
self.gameStatusRefHandle = self.gameResult.child(gameId).child((Auth.auth().currentUser?.uid)!).observe(DataEventType.value) { (snapshot) in
completion(snapshot.value as! Bool)
}
}
func removeObserverHandle(gameId: String) {
self.gameResult.child(gameId).child((Auth.auth().currentUser?.uid)!).removeObserver(withHandle: self.gameStatusRefHandle)
}
}
the setting of the handle and the removal are done in the SwiftUI view on the OnAppear and onDisappear
The above works when user navigates to the view and the onAppear function fires. When the user goes back and the onDisappear function fires I get the following error:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 2021-05-20 21:03:26.480564-0400 [33352:1962213] GameStore.swift:316: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Not sure why that is since as you can see I am setting the handle in the getPlayerStatus function
Related
I am trying to add a return type of a future in a closure. But the compiler is telling me that
`impl Trait` only allowed in function and inherent method return types, not in closure return
I have have also tried wrapping it in Box but that didn't work. I tried type aliases but they are nighly only features. I am unable to understand how else can I solve it.
pub async fn execute_event_handler(
event_handler: Option<Arc<PyFunction>>,
event_loop: Arc<Py<PyAny>>,
) -> Result<(), Box<dyn std::error::Error>> {
if let Some(handler) = event_handler {
match &(*handler) {
PyFunction::SyncFunction(function) => {
println!("Startup event handler");
Python::with_gil(|py| -> Result<(), Box<dyn std::error::Error>> {
function.call0(py)?;
Ok(())
})?;
}
PyFunction::CoRoutine(function) => {
let future = Python::with_gil(
|py| -> impl Future<Output = Result<Py<PyAny>, PyErr>> + Send { // this is giving an error
println!("Startup event handler async");
let coroutine = function.as_ref(py).call0().unwrap();
pyo3_asyncio::into_future_with_loop((*event_loop).as_ref(py), coroutine)
.unwrap()
},
);
future.await?;
}
}
}
Ok(())
}
PS: I want to wrap the closure output in an Result type and hence a return type is necessary.
Don't annotate the future.
If you need to annotate the enclosing type (e.g. Result), you can either annotate it when returning it (Result::<_, ErrorType>) or as the return type, but let inference find the future type itself with _: || -> Result<_, ErrorType>.
I have the following code that fetches a schedule
func fetchSchedule(completion: #escaping () -> ()) {
scheduleRef.queryOrderedByValue().queryEqual(toValue: true).observe(.value, with: { snapshot in
self.schedule = []
if snapshot.value is NSNull {
// Null
} else {
for child in snapshot.children {
if let snapshot = child as? DataSnapshot,
let schedule = Schedule(snapshot: snapshot) {
self.schedule.append(schedule)
}
}
}
})
}
The above get the current schedule but what I am unclear on is that i need that value to then call the next function call which get the associated games for that schedule on the .onAppear() of the view in SwiftUI
func getGames() {
scheduleStore.fetchSchedule()
//
gameStore.fetchGames(weekId: self.scheduleStore.schedule[0].weekId)
}
the gameStore.fetchGames always returns null, likely because it has not finished processing the fetchSchedule function?
How do I ensure the first function finishes before it calls the fetchGames?
You have a completion handler built into your function signature on fetchSchedule, but you aren't using it.
func fetchSchedule(completion: #escaping () -> ()) {
scheduleRef.queryOrderedByValue().queryEqual(toValue: true).observe(.value, with: { snapshot in
self.schedule = []
if snapshot.value is NSNull {
// Null
} else {
for child in snapshot.children {
if let snapshot = child as? DataSnapshot,
let schedule = Schedule(snapshot: snapshot) {
self.schedule.append(schedule)
}
}
completion() //<-- Here
}
})
}
Then,
func getGames() {
scheduleStore.fetchSchedule(completion: {
gameStore.fetchGames(weekId: self.scheduleStore.schedule[0].weekId)
})
}
You're not showing all of your code, but you may also have something broken between self.schedule, which you set in fetchSchedule, and self.scheduleStore, you you send to fetchGames -- make sure you've only got one place you're storing data -- should it be self.schedule in both places?
Update, based on comments
This code is approximate, since I don't have access to your types, but it should get you started:
func fetchSchedule(completion: #escaping ([Schedule]) -> ()) {
scheduleRef.queryOrderedByValue().queryEqual(toValue: true).observe(.value, with: { snapshot in
if snapshot.value is NSNull {
// Null
} else {
let schedules = snapshot.children.compactMap { child in
if let snapshot = child as? DataSnapshot, let schedule = Schedule(snapshot: snapshot) {
return schedule
}
return nil
}
completion(schedules)
}
})
}
func getGames() {
scheduleStore.fetchSchedule { schedules in
gameStore.fetchGames(weekId: schedules[0].weekId)
}
}
First-time poster, long-time reader ... I've wrapped an async call to the Firebase Authorization API. I'm calling it from inside a SwiftUI View function.
func authenticateFirebaseEmail(email: String, password: String) ->
Future<String, Error> {
return Future<String,Error> { promise in
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error=error {
print("failure detected")
promise(.failure(error))
}
if let result=result {
print("result detected - returning success promise")
promise(.success(result.user.email!))
}
}
}
}
...
func logMeInFuncInView() {
var cancellable : AnyCancellable?
cancellable = authenticateFirebaseEmail(email: self.userEmail, password: self.password).map( {
value in return value
})
.sink(receiveCompletion: { (completion) in
print("completion received")
}, receiveValue: { user in
print("value received")
self.errorMessage = user
})
}
The console output is as follows, but never reaches the "completion received" or "value received" calls:
result detected - returning successful promise
Is the issue with the wrapped callback, the future, the way I'm using the future, or something that I'm not seeing entirely?
Your cancellable is local variable, so destroyed once went off context. As soon as subscriber is destroyed it cancels subscription and, as it is only one, publisher cancelled as well.
Your solution is to make your cancellable as property, ie
var cancellable : AnyCancellable? // << here !!
func logMeInFuncInView() {
cancellable = authenticateFirebaseEmail(email: self.userEmail, password: self.password).map( {
value in return value
})
// .. other code
}
I have this problem. I'm receiving a notification from CloudKit using application:didReceiveRemoteNotification in AppDelegate. I'm able to receive the recordId, fetch it, and save it successfully. The problem is, the scene doesn't refresh.
final class UserData: ObservableObject {
#Published var items: [Item] = []
func saveFetchedItem(recordId: CKRecord.ID, reason: CKQueryNotification.Reason) {
fetchAndSaveRemoteDataFromCloudKit(recordId: recordId, reason: reason, userData: self)
}
}
in AppDelegate:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let notification: CKNotification = CKNotification(fromRemoteNotificationDictionary: userInfo)!
if notification.notificationType == .query {
let queryNotification = notification as! CKQueryNotification
let recordId = queryNotification.recordID
let reason: CKQueryNotification.Reason = queryNotification.queryNotificationReason
/// reasons:
/// .recordCreated, .recordUpdated, .recordDeleted
UserData().saveFetchedItem(recordId: recordId!, reason: reason)
}
completionHandler(.newData)
}
In fetchAndSaveRemoteDataFromCloudKit, I'm passing the UserData in order to save the item received and it works. The problem is, I show the items on a List and the list doesn't refresh, even when I print the userData.items and the new item is there.
Is there a connection between AppDelegate and SceneDelegate in order to refresh the scene/window?
I am trying to read nested data structures from Firebase Database, but I don't know how to manage the case when an object of type [String:AnyObject] could be nil.
When readFeesCleaner(callback_) is called, it throws an error.
func readFeesCleaner(callback: #escaping ((_ feesCleaner: FeesCleaner) -> Void)) {
dbRef.child("FeesCleaner").child(self.uidOfTextField!).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
guard !(snapshot.value is NSNull) else {
return
}
//throws error: signal SIGABRTCould not cast value of type '__NSDictionaryM' (0x1111152b0) to 'FIRDataSnapshot' (0x10ef16d18).
let feesCleanersReceived = FeesCleaner(snapshot: (snapshot.value)! as! FIRDataSnapshot)
callback(feesCleanersReceived)
}) { (error:Error) in
print(#line, "\(error.localizedDescription)")
}
}
struct FeesCleaner {
var outstandingFees: AnyObject!
var timeStampFeesSaved: [String:AnyObject]!
var backgroundCheck: AnyObject!
init(
outstandingFees: AnyObject? = nil, //value might not exist when reading
timeStampFeesSaved: [String:AnyObject]? = nil,// value might not exist when reading
backgroundCheck: AnyObject) {
self.outstandingFees = outstandingFees
self.timeStampFeesSaved = timeStampFeesSaved
self.backgroundCheck = backgroundCheck
}//end of init
//read data here
[full struct data here][1]
https://gist.github.com/bibscy/dc48f7107459379e045a50fdbbc35335
}//end of struct
There's a number of issues here. First:
how to manage the case when an object of type [String:AnyObject]
could be nil.
You've handled that with the prior statement, noting that you can also add
if snapshot.exists == false {return}
Second: You've got to handle optionals properly - if a var could be nil, you need code in place to handle that situation and not plow through it. If you force unwrap an optional you are essentially stating that for sure, it will never be nil, so basically, don't do that.
One fix could be to simply pass the snapshot as a DataSnapshot and then pull out the properties one at a time; if they exist, assign them, if not set to 0 or nil or some other placeholder.
Something like this inside the Firebase closure:
let feesCleanersReceived = FeesCleaner(withSnapshot: snapshot)
and then your struct like this: note we are leveraging the nil coalescing operator, ??
struct FeesCleanerStruct {
var outstandingFees: String?
var timeStampFeesSaved: String?
init(withSnapshot: DataSnapshot) {
let dict = withSnapshot.value as! [String: Any]
self.outstandingFees = dict["outstandingFees"] as? String ?? "0.0"
self.timeStampFeesSaved = dict["timeStampFeesSaved"] as? String ?? "0"
}
}