iOS Firebase - Crash in simulator but not device w/ persistence - ios

I tried googling this problem but it seems like everyone has the opposite problem where the app runs on simulator but not their device. I've been struggling a LOT all week with firebase asynchronous calls returning null and linked the issue to persistence being enabled. All my problems go away if I disable persistence, but I want it enabled. I learned recently about synchronous issues with the different listeners/persistence and have been struggling with firebase returning outdated/nil values for a while.
Simulator was working just a week or two ago and I'm not sure what's changed. I've tried messing with / switching out .observeSingleEvent for .observe and still crashes at this code:
let synced = ref.child("profiles").child((FIRAuth.auth()?.currentUser?.uid)!).child("level")
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
print(snapshot)
print(snapshot.ref)
if (snapshot.value as! String == "One") {
........//CRASH
With the message:
Could not cast value of type 'NSNull' (0x10b7cf8c8) to 'NSString' (0x10a9dfc40).
When I try to print snapshot, it shows me an empty snapshot. But when I print the ref, the link works and takes me to the right place in my db (where I can see the data exists)
Any ideas how to fix/get around this without disabling persistence? Or more importantly I guess, should I care that it doesn't work in simulator if it works on a device? Will this matter for app store approval / affect future users?
If you'd like to see for yourself that this is an issue of firebase getting a nil/outdated value when the reference exists, here is what I see when I follow the printed ref link

The error seems fairly explicit: there is no value, so you can't convert it to a string.
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if (snapshot.exists()) {
if (snapshot.value as! String == "One") {
........//CRASH

Related

Firebase's snapshot.value cast to Bool suddenly returning nil

So my code was working perfectly fine but today it is returning nil when I try to cast Firebase's snapshot.value to a Bool. I haven't made any change to code at all. But it works fine if I cast it to a String (as? String returns "true").
Same issue at another place where I was casting to [String: Bool].
One thing that is different from earlier, I installed Xcode 10 beta 3 today (Xcode 9.4 is installed too). Both Xcode versions are returning nil. Here's the code:
if let currentUserId = Auth.auth().currentUser?.uid{
Database.database().reference().child("users/\(currentUserId)/share").observe(.childAdded) { (snapshot) in
let data = snapshot.value as? Bool
if data == true{
self.showNotificationsBadge()
}
}
}
Edit: Snapshot is not nil. You can see that in the console pane. I have printed both snapshot.key and .value.
Edit 2: I'm sorry if my question caused any confusions. The main purpose of this question is to know the problem, is it Firebase SDK or Xcode. Because it was working perfectly till now. I know how I can tackle this and how can I convert a String to a Bool.
Edit 3: Thank you everyone for your answers. I'm sorry I was unable to deliver my thoughts correctly. I just want to know about this unpredicted behavior and why is it happening? Who is the culprit here? Xcode, Firebase SDK? This code worked PERFECTLY FINE for about a month. I'm not asking for a solution, I'm just curious about this behavior. It would be great if someone from team Firebase can explain this issue.
Edit 4: Removed image and added code. Also figured out the issue thanks to #Jay's comment.
You need check if the snapshot is nil before checking the value.
if let data = snapshot.value as? Bool {
....
}
This happened to me just recently. I discovered that I had quotes around the child value, eg. "true". Remove the quotes and the solution from #Maximo should work as expected.

Firebase - iOS suddenly returning null for snapshots that I know exist?

I came back to work on a project I have not touched in a week and suddenly my code was not working. I take a snapshot of data that I am sure exists (I can see it exactly in the database and changed no code) yet it is returning NSNULL for the snapshot and when I print snapshot.exists() I get false. I even tried printing snapshot.ref and typing that link into my browser and that takes me to the data on the console.
The only thing I did today was manually add a piece of data into my firebase console, but it was not in the same node where I am getting this error and it was working for a while after I made the change so I don't see how that could have caused this. Is anyone else having this issue or know why this would suddenly start happening?
Here is my code:
_ = ref.child("profiles").child((FIRAuth.auth()?.currentUser?.uid)!).child("type").observeSingleEventOfType(FIRDataEventType.Value, withBlock: { (snapshot) in
print(snapshot.exists())
print(snapshot.ref)
if (snapshot.value as! String == "Player") {
.....
}
})
I'm getting the error Could not cast value of type 'NSNull' (0x10d008600) to 'NSString' (0x10d3ffb48).' and as I said earlier snapshot.exists() is false and the ref link works.
Thanks!

Parse - callFunctionInBackground crashes app on [__NSTaggedDate UTF8String] - But did not when I submitted my app

I'm using a Parse Cloud function to get a list of movies. This was all working very well till today. The app crashes and I can't seem to fix it.
Weird (and highly problematic) thing is that it did work yesterday and the days before. (This app is in production in the App store)
This is the function causing the error:
PFCloud.callFunctionInBackground(function, withParameters: parameters) { (movies, error) -> Void in
if error != nil { return block(success: false, movies: []) }
PFObject.pinAllInBackground(movies as! [PFObject])
let foundMovies = ParseMovie.arrayFromPFObjects(movies as! [PFObject])
return block(success: true, movies: foundMovies)
}
If I comment the 4 lines inside the block, it still crashes. If I comment the entire function, it doesn't. So it seems as soon as I call "PFCloud.callFunction" the app crashes.
Any idea what can go wrong? The Parse cloud function does work, because calling it with the parse rest API gives me the movies as expected.
Any help is appreciated, thanks!
Ok, got it. Apparently I'm using an old version of the Parse SDK (1.5) and since today, this version is not working anymore.. I'm not using pods so I was not aware of this out-dated version. A bit disappointed, but probably my fault?

What is wrong with this line of Swift iOS Code?

I have created an iOS app using Swift and everything is working fine and dandy on the simulator. I get no errors or crashes at all, but when I submit my app to put up on the app store Apple rejects it and lets me know that it crashes when the user makes a selection. I cannot recreate this error/crash. I took the crash logs and symbolicated them. This line of code came up as the culprit for the crashes:
linksToPass = getLinks(season) as [String:[String]]
This line is trying to store the resulting Dictionary from the getLinks() function I created. It for sure is getting a dictionary and if there is no dictionary to send back I create a dictionary which has error information in it, so it is for sure returning a dictionary in that format no matter what. Seeing as I cannot recreate the crash, I am just trying to error check this line of code in any way possible so it does't crash when I resubmit to Apple.
I tried checking if the resulting dictionary was nil like so:
if(getLinks(seasons) != nil){
linksToPass = getLinks(season) as [String:[String]]
}
This is not valid though, and XCode lets me know that UInt8 is not compatible with NSDictionary or something of that nature.
I then fixed that line and changed it to this:
if(getLinks(seasons) != ["":[""]]){
linksToPass = getLinks(season) as [String:[String]]
}
I am just not sure if this is even a good way to check for errors. I was wondering if there were any suggestions on how I may go about making sure this line does not fail and result in a crash. Thank you very much.
EDIT:
Here is my getLinks() function if that helps add more info to the problem:
var season = ""
let hymn_links = Hymn_Links()
func getLinks (nameofseason:String) -> NSDictionary
{
switch (nameofseason)
{
default:
return ["Maps Not Found": []]
}
}
EDIT #2:
This is my updated getLinks() function with the use of optionals.
func getLinks (nameofseason:String) -> NSDictionary?
{
switch (nameofseason)
{
default:
return nil
}
}
Also in my statement of linksToPass I changed it to:
if let links = getLinks(season) as? [String:[String]]
{
linksToPass = links
hymnnames = [String] (linksToPass.keys)
}
There are some known issues with the Swift optimiser. Some people have resorted to shipping with debug builds.
My suggestion would be to test with an optimised build to see if you can reproduce it. You can then try shipping a debug build to the App store.
General Code Comments
Why are you returning an NSDictionary rather than a Swift dictionary anyway? Without knowing the contents and creation method for your hymn_links object I can't be sure how good it is.
I would avoid as casts until Swift 1.2 and stick to using as? and then handling the nil case. At least in your "Edit 2" a nil will cause a crash as nil cannot be cast to [String:[String]] although [String:[String]]? should be possible.
Can you guarantee that all of the items returned by the switch statement will never under any circumstances be nil? If not getLinks should return an Optional.
Note that is is virtually impossible for getLinks to know that one of the items will never be nil and in Swift un-handed nils are a crash waiting to happen. Unless all these methods correctly handle nil.
Return an Optional and handle that in the statement that calls getLinks.
Languages handle nils differently, Objective-C handles them rather well, Java and Swift by crashing. But Swift has a mechanism to handle nils without crashing: Optionals, use it.

iOS AddressBook Swift BadAccess

So here's the deal:
I am building an app that accesses the internal addressbook on the iPhone. Everything was working fine (and is working fine on the simulator still), but now on the device I get a ThreadBreak and lldb opens up with no error message in the console on this line:
tempPerson.firstname = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as String? ?? ""
Again, this is still working on the simulator mind you and used to work just fine.
There is some multithread action going on because I am loading the addressbook into memory in the background using GCD above:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
My addressbook was loaded properly. I have deleted the app from my test device (iPhone 6), cleaned the project (figuring it could be a bad link between some Obj-C bridge file that didn't like the ABAddressBook datatype, and even hopped on one foot while holding the phone Northward for good luck. Nothing worked.
I just can't figure out why it would work on all the Simulators and not on the phone. I do, however, notice that the app is not asking permission to access the contacts when it firsts opens after deleting (though the Authorization check is called and is granting authorization):
case .Authorized:
return self.createAddressBook()
I'm at a loss at even what to show you all since it's not returning much of anything, but here is a breakdown of the Thread Error (NOTE: Sometimes it happens on different threads...)
Thread 4
Queue: com.apple.root.background.-qos (concurrent)
0 swift_getObjectType
1 swift_dynamicCast
2 SomeApp.ViewController.(createAddressBook(SomeApp.ViewController) -> () -> Swift.Bool).(closure #1)
3_Dispatch_call_block_and_release
4_dispatch_client_callout
5_dispatch_root_queue_drain
6_dispatch_worker_thread3
7_pthread_wqthread
Enqueued from com.apple.main-thread (Thread 1)
0_dispatch_async_f_slow
*>> 1_SomeApp.ViewController.createAddressBook(SomeApp.ViewController)() -> Swift.Bool [inlined]
2_SomeApp.ViewController.determineStatus(SomeApp.ViewController)() -> Swift.Bool
3_SomeApp.ViewController.loadContacts(SomeApp.ViewController() -> ()
4_SomeApp.ViewController.viewDidLoad(SomeApp.ViewController() -> ()
... etc Viewloading stuff
On the breakpoint, the two variables given in the debugger are
The addressbookref
adbk = (AnyObject?) (instance_type = Builtin.RawPointer = 0x... EvaluatingTo: Some
and the ViewController called "Self"
self(SomeApp.ViewController)
containing all the variables my app uses elsewhere.
Sorry for being so verbose, but I really don't know what of this is relevant and what isn't..
Thanks for your help in advance!
Update: I turned off multithreading/ dispatching and it didn't work. So it must be something to do with that tempPerson.firstname bit.
Update 2: It does go around the loop twice and get two contacts before failing. I think it is hitting a nil value that isn't quite nil for some of the contacts and then crashing.. I do have the following check for it though and it is passing through:
if ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as AnyObject? != nil{
&
if ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as AnyObject? != nil{
respectively.
Update 3: Okay. So Now it seems whenever there is a blank field for first or last name it crashes (on the simulator now too). Which really sucks because I thought I put in a good check to make sure I am not accessing nil valued properties. Whats even more annoying is that I haven't changed this code and it was working fine until I removed some derived data and changed the test file to avoid a linker error. Anything that could happen in the build process that is mucking up my code?
Basically, you need to really check your type casts when accessing the addressbook, see:
tempPerson.lastname = ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as NSObject as? String ?? ""
The problem is that some people have really dirty addressbooks for a variety of reasons and even have some mixed up properties like having a last name property that contains "LastName, FirstName SPACE Birthday" with no firstname property.
For whatever reason, typecasting first as an NSObject BEFORE as a String seems to work well.

Resources