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
Related
Saving NSMutableArray with NSUserDefaults in Swift Returns Nil
setObject method removed my codes like that and not work. i try all set types still not work. Any idead ?
var banksAccount : NSMutableArray = []
for bankJson in (self.users?[0].Banks)! {
self.banksAccount.add(bankJson)
}
UserDefaults.standard.set(self.banksAccount, forKey: "myBanksAccounts")
UserDefaults.standard.synchronize()
For saving
let cacheArrayData = NSKeyedArchiver.archivedData(withRootObject: banksAccount)
UserDefaults.standard.set(cacheArrayData, forKey: "EventItems")
For retrieving
let retriveArrayData = UserDefaults.standard.object(forKey: "EventItems") as? NSData
let retriveArray = NSKeyedUnarchiver.unarchiveObject(with: retriveArrayData! as Data) as? NSArray
self.eventsArray = NSMutableArray(array:retriveArray!)
Xcode7 and swift, My code:
func loadDefaults() {
let settingBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")
if settingBundle == nil {
return
}
let root = NSDictionary(contentsOfFile: settingBundle!.stringByAppendingString("Root.plist"))
let prefrences = root?.objectForKey("PreferenceSpecifiers") as! Array<NSDictionary>
let defautlsToRegister = NSMutableDictionary(capacity: root!.count)
for prefrence in prefrences {
let key = prefrence.objectForKey("Key") as! String!
if key != nil {
defautlsToRegister.setValue(prefrence.objectForKey("DefaultVale"), forKey: key!)
}
}
NSUserDefaults.standardUserDefaults().registerDefaults(defautlsToRegister as [NSObject: AnyObject])
}
Problem code:
NSUserDefaults.standardUserDefaults().registerDefaults(defautlsToRegister as [NSObject: AnyObject])
building warnings
Cannot convert value of type '[NSObject : AnyObject]' to expected argument type '[String : AnyObject]'
change code:
NSUserDefaults.standardUserDefaults().registerDefaults(defautlsToRegister as [String: AnyObject])
building warnings
'NSMutableDictionary' is not convertible to '[String : AnyObject]'
Please teach me how to do? thanks.
Your defautlsToRegister should be in the following format [String: AnyObject]
Example: The following should work without warning
let defautlsToRegister = ["Test":10]
NSUserDefaults.standardUserDefaults().registerDefaults(defautlsToRegister as [String: AnyObject])
I've noticed a simple thing about this error. I'm not sure if this the case but casting String to NSString seems to solve the problem for me. I found an explanation that AnyObject is a type alias to represent instances of any reference type, which is for example: NSString. But String is a struct so it can't be the reference type for AnyObject.
I see two ways for this:
First:
let keyForMyKey: NSString = NSString(string: "mykey")
let result = dict.objectForKey(keyForMyKey) as? NSMutableArray
Second:
let result = dict.objectForKey(NSString(string: "myKey")) as? NSMUtableArray
More on the problem here: http://drewag.me/posts/swift-s-weird-handling-of-basic-value-types-and-anyobject
For me this is worked.
let token = fields["X-AUTH-TOKEN"]! as? [[String : AnyObject]] //{
// safe to use employees
self.bindings.setObject(NSString(format: "%#", token!) as String, forKey: "X-AUTH-TOKEN")
Format(NSString method) will work in this scenario.
Dictionary is a bridged type, why is it that I can switch from Swift dictionary to NSDictionary but not the other way around? (Compile Error: NSDictionary is not convertible to 'Dictionary')
According to Apple's doc:
All NSDictionary objects can be bridged to Swift dictionaries, so the Swift compiler replaces the NSDictionary class with [NSObject: AnyObject] when it imports Objective-C APIs.
import Foundation
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict:Dictionary = nsDict
This is correct but you have to perform a type safe cast from NSDictionary to a Dictionary
var swiftDict = ["0": "zero"]
let nsDict: NSDictionary = swiftDict
let backToSwiftDict: Dictionary = nsDict as Dictionary
... or you can cast it back into a dictionary with type-safe fields ...
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict = nsDict as! [String:String]
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
}
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()
}
}
})