Swift class cast fails while casting AnyObject to NSMutableDictionary - class

var fetchEachStory = Firebase(url: "\(self.individualStoryUrl)\(eachStory)")
// Read data and react to changes
fetchEachStory.observeEventType(.Value) {
snapshot in
let storyDetails = snapshot.value as NSMutableDictionary?
let score = storyDetails!["score"] as Int?
var thisStory = eachStory
if score? > self.minSetScore {
self.showStories[thisStory] = storyDetails
}
}
While Assigning Snapshot.value to story details, it fails some times saying:
error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).
The process has been returned to the state before expression evaluation.
How can I handle this ?

This line of code:
let storyDetails = snapshot.value as NSMutableDictionary?
fails if snapshot.value is not an NSMutableDictionary. It is better to use a conditional cast as? along with optional binding if let to make sure you only proceed if snapshot.value is the type you expect. The same is true for score being an Int.
According to the Firebase Documentation:
value
Returns the contents of this data snapshot as native types.
#property (strong, readonly, nonatomic) id value Return Value The data
as a native object.
Discussion Returns the contents of this data snapshot as native types.
Data types returned: * NSDictionary * NSArray * NSNumber (also
includes booleans) * NSString
Declared In FDataSnapshot.h
So you should be checking for NSDictionary instead of NSMutableDictionary.
I recommend:
// Read data and react to changes
fetchEachStory.observeEventType(.Value) {
snapshot in
if let storyDetails = snapshot.value as? NSDictionary {
// We know snapshot.value was non-nil and it is an NSDictionary.
if let score = storyDetails["score"] as? Int {
// We know "score" is a valid key in the dictionary and that its
// type is Int.
var thisStory = eachStory
if score > self.minSetScore {
self.showStories[thisStory] = storyDetails
self.tableView.reloadData()
}
}
}
}

I found the bug , It's more to do with FireBase FDataSnapshot , Should check if snapshot is nil , with snapshot.exists()
var fetchEachStory = Firebase(url:"\(self.individualStoryUrl)\(eachStory)")
// Read data and react to changes
fetchEachStory.observeEventType(.Value, withBlock: {
snapshot in
if snapshot.exists(){
let storyDetails = snapshot.value as NSDictionary?
let score = storyDetails!["score"] as Int?
var thisStory = eachStory
if score? > self.minSetScore{
self.showStories[thisStory] = storyDetails
self.tableView.reloadData()
}
}
})

Related

Problem Accessing Items Within NSDictionary Swift

I'm a newb to Swift programming, but experience in other languages.
I am having problem accessing items within NSDictionary to build out view elements. This is coming back from a Firebase instance.
Can someone take a look at the code and the output and lead me in the right direction to access these object properties?
ref.observe(.value, with: { (snapshot) in
for child in snapshot.children { //even though there is only 1 child
let snap = child as! DataSnapshot
let dict = snap.value as? NSDictionary
for (joke, item) in dict ?? [:] {
print(joke)
print(item)
}
}
})
This is the output from the print() methods.
joke2
{
PostUser = "Bobby D";
Punchline = "His money went to the movies.";
Rating = 1;
Setup = "Why did the dad go hungry?";
}
joke
{
PostUser = "Billy G";
Punchline = "Because he couldn't moo to a job.";
Rating = 3;
Setup = "Why did the cow go to school?";
}
Can someone tell me how to create items from these objects? Something like:
var posterName = joke.PostUser
When I try this, I get the error Value of type 'Any' has no member 'PostUser'. I've tried to access these DB object properties in multiple different ways described on SO and can't get any further.
I would recommend you to convert the output into objects like this:
struct Item {
var postUser: String?
var punchline: String?
var rating: Int?
var setup: String?
init(fromDict dict: [String: AnyObject] ) {
self.postUser = dict["PostUser"] as? String
self.punchline = dict["Punchline"] as? String
self.rating = dict["Rating"] as? Int
self.setup = dict["Setup"] as? String
}
}
And use it like this:
ref.observe(.value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
guard let dict = snap.value as? [String: AnyObject] else { continue }
let myItem = Item(fromDict: dict)
print(myItem)
}
})
But you could also access items in your dictionary directly like this:
let posterName = joke["PostUser"] as? String

NSNumber swift Dictionary to userdefaults swift3

I am trying to save swift dictionary [NSNumber : NSNumber] to UserDefaults. I have already tried to cast it as NSDictinonary, but still, the application crashes when I use set() function.
Key is a beacon minor key (NSNumber)
Value is an NSTimeinterval cast as NSNumber
the crash:
libc++abi.dylib: terminating with uncaught exception of type
NSException
var recentBeacons: [NSNumber : NSNumber] = [:]
func saveRecentBeaconDict()
{
let recentBeaconKeys = recentBeacons.keys
print("keys type is \(String(describing: recentBeaconKeys.self))")
let recentBeaconsNSDict = recentBeacons as NSDictionary
UserDefaults.standard.set(recentBeaconsNSDict, forKey:"recentBeacons")
}
prints out: keys type is LazyMapCollection< Dictionary < NSNumber, NSNumber >, NSNumber >(_base: [46171: 1501585588.173543], _transform: (Function))
Try with below answer.
var recentBeacons: [NSNumber : NSNumber] = [:]
func saveRecentBeaconDict()
{
let archivedObject = NSKeyedArchiver.archivedData(withRootObject: recentBeacons as NSDictionary)
UserDefaults.standard.set(archivedObject, forKey: "recentBeacons")
let data = UserDefaults.standard.object(forKey: "recentBeacons") as? Data
if let _data = data {
let allData = NSKeyedUnarchiver.unarchiveObject(with: _data) as! NSDictionary
print(allData)
}
}
You can't save Dictionary of type [NSNumber : NSNumber], allowed only [String, AnyObject] or [NSObject : AnyObject].
As approach you can convert your NSNumber's to String's

Swift Cannot assign through subscript: subscript is get only

I am fairly new to the Swift syntax and am receiving this error with my code "Cannot assign through subscript: subscript is get only"
This is from the line: friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
Any advice on the correct way of doing it would be very helpful.
func getFriendsUIDs() {
if FBSDKAccessToken.currentAccessToken() == nil {
print("failed to start graph request")
return
}else{
}
if FBSDKAccessToken.currentAccessToken() != nil {
}
let parameters = ["fields": "name, id, picture"]
FBSDKGraphRequest(graphPath: "/me/friends", parameters: parameters).startWithCompletionHandler {
(NSURLConnection, result, requestError) in
let friendIds = result["id"] as? NSDictionary
let friendsData = friendIds!["data"] as? [NSDictionary]
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("friendUIDs").observeEventType(.Value, withBlock: { (snapshot) in
self.FriendUIDs = NSArray()
self.FriendUIDs = (snapshot.value as? NSArray)!
print(self.FriendUIDs)
var friendDictionary = NSDictionary()
for friendUID in self.FriendUIDs {
friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
}
self.fetchFriendFeed(friendDictionary)
}) { (error) in
print(error.localizedDescription)
}
}
}
func fetchFriendFeed(friendDictionary: NSDictionary) {
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
for friendUID in FriendUIDs {
ref.child("users").child(friendUID as! String).child("Agenda").observeEventType(.ChildAdded, withBlock: { (snapshot) in
print(snapshot)
if let dictionary = snapshot.value as? [String: AnyObject] {
let friendPost = FriendPost()
friendPost.picture = friendDictionary[friendUID as! String]? ["picture"] as? String
friendPost.activity = dictionary["activity"] as? String
friendPost.date = dictionary["date"] as? String
friendPost.time = dictionary["time"] as? String
friendPost.friendname = friendDictionary[friendUID as! String]? ["name"] as? String
self.friendPosts.append(friendPost)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView?.reloadData()
Nothing to do with Swift. You've elected to use Objective-C, in effect, by making friendDictionary an NSDictionary. NSDictionary is immutable; you can't assign into it or alter it in any way. That is simply a fact about Objective-C. The Swift var declaration makes no difference to this fact.
A better choice, since you are writing in Swift, would be to use a Swift dictionary, which is [AnyHashable:Any]() (in Swift 3). This will interchange with NSDictionary when you are talking to Objective-C, but it will give you a mutable dictionary because you (rightly) declared it with var.
Have you tried using NSMutableDictionary? That solved the issue for me.
For those who get stuck here, another reason for this happens when you try to assign something that does not conform the actual dictionary, in my example i was doing something like this:
var dict = [Date : UUID]()
let randomUUID = UUID()
dict[randomUUID] = Date.now
whereas I meant to write UUID : Date but I was sleepy so i made a mistake, and Swift gave me a misleading error saying subscript is get-only. So this error also appears with type mismatch for Swift 5.7.

Could not cast value of type '__NSDictionaryM' to custom User Class using new Firebase

I'm attempting to query for a specific user using the new Firebase like so:
DataService.sharedInstance.REF_USERS.queryOrderedByChild("username").queryEqualToValue(field.text).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let userDoesNotExist = snapshot.value as? NSNull {
print("No User found!")
} else {
let theUsr = snapshot.value as! User
print(theUsr)
}
}, withCancelBlock: { (error) in
// Error
})
From there I was looking to store the snapshot into its own object and access its values from there. I was attempting to do so by doing this:
class User {
let key: String!
let ref: FIRDatabaseReference?
var username: String!
var email: String!
// Initialize from data snapshot
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
email = snapshot.value!["email"] as! String
username = snapshot.value!["username"] as! String
ref = snapshot.ref
}
func toAnyObject() -> AnyObject {
return [
"email": email,
"username": username,
]
}
}
The problem I'm running into is a crash with the following:
Could not cast value of type '__NSDictionaryM' (0x10239efc0) to 'Scenes.User' (0x100807bf0).
How do I fix this? I don't remember running into this problem on the old Firebase. I need access to the snapshot.value's key and for some reason can't access it by using snapshot.value.key without getting a crash so I figure I'd try passing all the data into it's own object.
snapshot.value isn't a User, it's a dictionary (*)
Here's a couple of options - there are many.
let aUser = User()
aUser.initWithSnapshot(snapshot)
then, within the User class, map the snapshot.values to the user properties
or
let aUser = User()
aUser.user_name = snapshot.value["user_name"] as! String
aUser.gender = snapshot.value["gender"] as! String
Oh, and don't forget that by using .Value your snapshot may return multiple child nodes so those will need to be iterated over
for child in snapshot.children {
let whatever = child.value["whatever"] as! String
}
In your case it's just one user so it's fine they way you have it.
*It could be
NSDictionary
NSArray
NSNumber (also includes booleans)
NSString

Swift - Why can I not return an NSNumber or Double data type from this NSDictionary object?

The second line of the code segment below returns an error unless I change the portion that reads "as NSNumber" to "as String". The value returned in rowData["lngID"] is a numeric value. Can someone please explain this to me?
let rowData: NSDictionary = objReport as NSDictionary
let lngReportID = rowData["lngID"] as NSNumber
What I'm actually attempting to do here is take a JSON response and load it into an array of objects as follows. Perhaps there is a better way to achieve this. Any suggestions for a better approach is much appreciated. First, the function didReceiveAPIResults returns the results to the app. Then the function loadReportsIntoArray is called.
func loadReportsIntoArray(pReports: NSArray) {
arrayPoints = []
for (intRow, objReport) in enumerate(pReports) {
// index is the index within the array
// participant is the real object contained in the array
let rowData: NSDictionary = objReport as NSDictionary
let lngReportID = rowData["lngID"] as NSNumber
let lngReportTypeID = rowData["lngTypeID"] as NSNumber
let strOtherTypeName = rowData["strOtherTypeName"] as String
let strDescription = rowData["strDescription"] as String
let dtmFirstReport = rowData["dtmFirstReport"] as String
let dblLat = rowData["dblLat"] as NSNumber
let dblLong = rowData["dblLong"] as NSNumber
let strReportedByUsername = rowData["strReportedByUsername"] as String
let lngReportedByID = rowData["lngReportedBy"] as NSNumber
let lngCommentCount = rowData["lngCommentCount"] as NSNumber
let lngNumLikes = rowData["lngNumLikes"] as NSNumber
let blnUserLikedEvent = rowData["blnUserLikedEvent"] as Bool
var objReport = Report(plngReportID: lngReportID, plngReportTypeID: lngReportTypeID, pstrOtherTypeName: strOtherTypeName, pstrDescription: strDescription, pdtmFirstReport: dtmFirstReport, pdblLat: dblLat, pdblLong: dblLong, pstrReportedByUsername: strReportedByUsername, plngReportedByID: lngReportedByID, plngCommentCount: lngCommentCount, plngNumLikes: lngNumLikes, pblnUserLikedEvent: blnUserLikedEvent)
//arrayPoints.append(objReport)
}
}
func didReceiveAPIResults(results: NSDictionary) {
var success: NSInteger = results["success"] as NSInteger
if success == 1 {
var resultsArr = results["geopoints"] as NSArray
dispatch_async(dispatch_get_main_queue(), {
self.loadReportsIntoArray(resultsArr)
})
}
else {
// Error occurred
}
}
I was able to recreate your error using the following code:
let objReport = NSDictionary(object: "string", forKey: "lngID")
let rowData: NSDictionary = objReport as NSDictionary
let lngReportID = rowData["lngID"] as NSNumber // Error
However, changing the objReport to NSDictionary(object: NSNumber(integer: 0), forKey: "lngID") solved the problem. Therefore, I think your problem is the object stored for the key lngID isn't an NSNumber.
For the solution to this you should look at Kumar Nitin's answer to check you've got a number stored, or you could use the code, they both do the same thing pretty much:
if let lngID = rowData["lngID"] as? NSNumber {
// Do stuff with lngID.
}
In swift, you don't have NSNumber, however you can use the Obj C's NSNumber if need be.
The above code for NSNumber should be as follows if you are expecting a double or float or int. Add a check to ensure the value is not nil, or else it will crash your app.
if let lngReportID = rowData["lngID"] as? Int {
//Do the task required
}