5

First, I have checked these answers that do not help me : Swift JSON error, Could not cast value of type '__NSArrayM' (0x507b58) to 'NSDictionary' (0x507d74)

Get data from Firebase

When retrieving data from Firebase (3.x), I have an error that occurs which is :

Could not cast value of type '__NSArrayM' (0x10ca9fc30) to 'NSDictionary' (0x10caa0108).

with this code and tree :

Tree :

enter image description here

Retrieving function :

func retrievePlanes() {

    print("Retrieve Planes")

    ref = FIRDatabase.database().reference(withPath: "results")

    ref.observe(.value, with: { snapshot in

        var newItems: [Planes] = []

        for item in snapshot.children {
            let planesItem = Planes(snapshot: item as! FIRDataSnapshot)
            newItems.append(planesItem)
        }

        self.planes = newItems
        self.tableView.reloadData()

    })

}

Planes.swift - To manage the data

import Foundation
import Firebase
import FirebaseDatabase

struct Planes {

    let key: String!
    let name: String!
    let code:String!
    let flightRange: Int?
    let typicalSeats: Int?
    let maxSeats: Int?
    let wingSpan: String!
    let takeoffLength: Int?
    let rateClimb: Int?
    let maxCruiseAltitude: Int?
    let cruiseSpeed: String!
    let landingLength: Int?
    let engines: String!
    let votes: Int?
    let data: String!
    let imagePlane:String!
    let imageTakenFrom: String!
    let ref: FIRDatabaseReference?

    init(name: String, code: String, flightRange: Int, typicalSeats: Int, maxSeats: Int, wingSpan: String, takeoffLength: Int, rateClimb: Int, maxCruiseAltitude: Int, cruiseSpeed: String, landingLength: Int, engines: String, votes: Int, data: String, imagePlane: String, imageTakenFrom: String, key: String = "") {

        self.key = key
        self.name = name
        self.code = code
        self.flightRange = flightRange
        self.typicalSeats = typicalSeats
        self.maxSeats = maxSeats
        self.wingSpan = wingSpan
        self.takeoffLength = takeoffLength
        self.rateClimb = rateClimb
        self.maxCruiseAltitude = maxCruiseAltitude
        self.cruiseSpeed = cruiseSpeed
        self.landingLength = landingLength
        self.engines = engines
        self.votes = votes
        self.data = data
        self.imagePlane = imagePlane
        self.imageTakenFrom = imageTakenFrom
        self.ref = nil

    }

    init(snapshot: FIRDataSnapshot) {

        ref = snapshot.ref
        key = snapshot.key
        let snapshotValue = snapshot.value as! [String:AnyObject]
        name = snapshotValue["name"] as! String
        code = snapshotValue["code"] as! String
        flightRange = snapshotValue["intFlightRange"] as? Int
        typicalSeats = snapshotValue["intTypicalSeats"] as? Int
        maxSeats = snapshotValue["intMaxSeats"] as? Int
        wingSpan = snapshotValue["wingSpan"] as! String
        takeoffLength = snapshotValue["intTakeoffLength"] as? Int
        rateClimb = snapshotValue["intRateClimb"] as? Int
        maxCruiseAltitude = snapshotValue["intMaxCruiseAltitude"] as? Int
        cruiseSpeed = snapshotValue["cruiseSpeed"] as! String
        landingLength = snapshotValue["intLandingLength"] as? Int
        engines = snapshotValue["engines"] as! String
        votes = snapshotValue["votes"] as? Int
        data = snapshotValue["data"] as! String
        imagePlane = snapshotValue["planeImage"] as! String
        imageTakenFrom = snapshotValue["imageTakenFrom"] as! String
    }

on the line : let snapshotValue = snapshot.value as! [String:AnyObject]

I suppose that is due to the snapshot value that can't be retrieved under [String:AnyObject] because of the Int below. (It is working when I only have String in another case).

I also know that Firebase "converts" the JSON tree to these objects [link]:

  • NSString
  • NSNumber
  • NSArray
  • NSDictionnary

but I can't figure out what has to be changed in the snapshot.value line to make it work.

Thanks for your help.

EDIT : I just sent a troubleshooting request. Will post updates. EDIT 2: See Jay's answer. In my case the tree was wrong.

Antoine
  • 1,139
  • 9
  • 21

5 Answers5

3

I took your code and shrunk it down a bit for testing, and it's working. (note Firebase 2.x on OS X and Swift 3 but the code is similar)

Firebase structure:

  "what-am" : {
    "results" : [ {
      "code" : "738/B738",
      "data" : "Boeing",
      "engines" : "Rolls"
    }, {
      "code" : "727/B727",
      "data" : "Boeing",
      "engines" : "Pratt"
    } ]
  }

Here's the Planes struct

struct Planes {

    var code:String!
    var data: String!
    var engines: String!

    init(code: String, data: String, engines: String ) {

        self.code = code
        self.data = data
        self.engines = engines
    }

    init(snapshot: FDataSnapshot) {

        let snapshotValue = snapshot.value as! [String:AnyObject]

        code = snapshotValue["code"] as! String
        data = snapshotValue["data"] as! String
        engines = snapshotValue["engines"] as! String
    }
}

and then the code that reads in two planes, populates and array and then prints the array.

let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!

ref.observe(.value, with: { snapshot in

        if ( snapshot!.value is NSNull ) {
            print("not found")
        } else {

            var newItems: [Planes] = []

            for item in (snapshot?.children)! {
                let planesItem = Planes(snapshot: item as! FDataSnapshot)
                newItems.append(planesItem)
            }

            self.planes = newItems
            print(self.planes)

        }
})

and finally the output

[Swift_Firebase_Test.Planes(code: 738/B738, data: Boeing, engines: Rolls),
 Swift_Firebase_Test.Planes(code: 727/B727, data: Boeing, engines: Pratt)]

Key and name are nil as I removed then from the Planes structure.

The line you asked about

let snapshotValue = snapshot.value as! [String:AnyObject]

is valid as the snapshot contains a series of key:value pairs so String:AnyObject works.

This line changed due to Swift 3

for item in (snapshot?.children)!

but other than that, the code works.

Try this to ensure you are reading the correct node. This reads the above structure and prints out each engine type. (tested and works)

 let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!
 ref.observe(.value, with: { snapshot in
      if ( snapshot!.value is NSNull ) {
           print("not found")
      } else {
           for child in (snapshot?.children)! {
                let snap = child as! FDataSnapshot
                let dict = snap.value as! [String: String]
                let engines = dict["engines"]
                print(engines!)
           }    
      }
 })
Jay
  • 34,438
  • 18
  • 52
  • 81
  • `.child(byAppendingPath: )` is deprecated. – eshirima Oct 10 '16 at 18:24
  • So I should change the snapshotValue line to the for item in (snapshot?.children) ? – Antoine Oct 10 '16 at 18:53
  • @EmilDavid Yes, I realize that is depreciated per my note at the top (Firebase 2.x) however, Firebase says they 'still support' 2.x for 'some time going forward' and when you have a codebase on macOS, you can't use 3. – Jay Oct 10 '16 at 21:43
  • @antoinebelldev I had to do that for iteration change in Swift 3. In your case, using for item in snapshot.children was the correct sequence in Swift 2. I added a suggestion to the end of my answer. – Jay Oct 10 '16 at 21:51
  • Not working. for 'let engines...' line I have the error : Type Any has no subscript members – Antoine Oct 13 '16 at 16:22
  • @antoinebelldev Wow - sorry. I typed that in without testing and sent you in the wrong direction. I removed that code in my answer and added tested code at the end which works properly with the data structure in my answer. You can reduce the code but i left it verbose to give you a feel what's going on. Give it a try. – Jay Oct 13 '16 at 18:12
  • Thanks I'll try that – Antoine Oct 14 '16 at 05:32
  • I tried your solution and the line with "let dict..." doesn't work. Thanks for trying again – Antoine Oct 16 '16 at 12:17
  • @antoinebelldev Can you be more specific? I have that exact code running and working with the structure I presented. Are you getting an error or possibly using another Firebase structure? If so, create a new node and use the structure in my answer. – Jay Oct 16 '16 at 15:06
  • Hello @Jay ! Got answer from support, just saw my tree was wrong. Anyway, your answer is full functional. Thank you so much. – Antoine Oct 19 '16 at 15:02
  • You can use `snapshot.exists()` to check if the snapshot is null, just a note. – Josh Feb 20 '18 at 20:27
0

In your struct class make sure of these things:-

  • Avoid declaring your variables as :Int? because that's practically nil, change them to :Int!

  • Your key in your firebase is an Int and you are declaring your key in struct as let key: String!, Change it to let key: Int!

  • Prefer your snapshot dictionary declaration as let snapshotValue = snapshot.value as! [AnyHashable:Any] (as per swift 3)

Then your init function to :-

Just change the line

let snapshotValue = snapshot.value as! [String:AnyObject]

To

let snapshotValue = (snapshot.value as! NSArray)[0] as! [String:AnyObject]
Dravidian
  • 9,945
  • 3
  • 34
  • 74
0

I think you are having an extra array in your results key-value on the firebase data.

  • You should try removing that array or

  • You may retrieve dictionary from first index of the array like;

    // .. your code
    let snapshotValue = (snapshot.value as! [AnyObject])[0] as! [String:AnyObject];
    // .. your code
    
Shoaib
  • 2,286
  • 1
  • 19
  • 27
0

update FIRDataSnapshot to DataSnapshot Swift 4

Osman
  • 1,496
  • 18
  • 22
0

Below is an example for Swift 4. Where you need to change FIRDataSnapshot to DataSnapshot

    func fetchChats(chatId: String) {
    ref.child("chats").child("SomeChildId").observe(.value) { (snapshot) in
        for child in snapshot.children {
            let data = child as! DataSnapshot //<--- Update this line
            let dict = data.value as! [String: AnyObject]
            let message = dict["message"]
            print(message!)
        }
    }
}
Aaron A
  • 225
  • 2
  • 7