I have been using this extension to successfully map my Realm Results to NSDictionary:
extension Object {
func toDictionary() -> NSDictionary {
let properties = self.objectSchema.properties.map { $0.name }
let dictionary = self.dictionaryWithValuesForKeys(properties)
let mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeysWithDictionary(dictionary)
for prop in self.objectSchema.properties as [Property]! {
// find lists
if let nestedObject = self[prop.name] as? Object {
mutabledic.setValue(nestedObject.toDictionary(), forKey: prop.name)
} else if let nestedListObject = self[prop.name] as? ListBase {
var objects = [AnyObject]()
for index in 0..<nestedListObject._rlmArray.count {
let object = nestedListObject._rlmArray[index] as AnyObject
objects.append(object.toDictionary())
}
mutabledic.setObject(objects, forKey: prop.name)
}
}
return mutabledic
}
}
But I am now trying to map :
let allObjectLists = realm.objects(UseItemList.self)
let firstObject = allObjectLists[0].valueForKey("useItems")
let toDict = firstObject?.toDictionary() //error here
How do I fix this, there must be a way to map allObjectLists[0].valueForKey("useItems") to a Dictionary
Here is the Error I get:
2016-11-10 11:45:09.056 CPS Stocker[6187:167500] -[_TtGC10RealmSwift4ListC11CPS_Stocker7UseItem_ toDictionary]: unrecognized selector sent to instance 0x7fa7f3a49650
2016-11-10 11:45:09.253 CPS Stocker[6187:167500] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtGC10RealmSwift4ListC11CPS_Stocker7UseItem_ toDictionary]: unrecognized selector sent to instance 0x7fa7f3a49650'
And here is my UseItemList Object class:
class UseItemList: Object {
dynamic var dateCreated = NSDate()
dynamic var locationUnique = Int()
dynamic var MainActivityReference1 = ""
dynamic var MainActivityReference2 = ""
let useItems = List<UseItem>()
}
Here is how I fixed it :
let firstObjectInList = allObjectLists.first!
let useItemsInFirstObject = firstObjectInList.useItems
for firstUseItem in useItemsInFirstObject {
let dit = firstUseItem.toDictionary()
usedObjectDictionaries.append(dit)
}
You're not accessing an Object there.
That line of code let firstObject = allObjectLists[0].valueForKey("useItems") is pulling out the useItems object, which is a List object. This is why it's reporting there's no method named toDictionary() available for it.
If you're trying to get the first object in useItems to generate a dictionary off that, it should be:
let allObjectLists = realm.objects(UseItemList.self) // Get all 'UseItemList' objects from Realm as a `Results` object
let firstObjectInList = allObjectLists.first! // Get the first UseItemList object from the 'Results' object
let useItemsInFirstObject = firstObjectInList.useItems // Access the 'useItems' List object in the first object
let firstUseItem = useItems.first! // Access the first item from the 'useItems' List object
let toDict = firstItem.toDictionary() // Convert the first item into an array
Obviously you can condense this down into one line of code, but you need to make sure you're accessing all of the elements in the right order or else you won't get to a proper Object at the end. :)
Related
i'm trying to store data from API in my NSObject class and then save it to core data. The response have one variable with data type TCHMembers, which is of twilio class, this TCHMembers is a NSObject class and it has some methods that has to be call in further code. Now when i try to store it in core data entity through NSKeyedArchive the app go to catch bllock and throughs an error, NSDebugDescription=Caught exception during archival: -[TCHMembers encodeWithCoder:]: unrecognized selector sent to instance . How i can store it in my core data, i think data type is giving this error, how i can store that?
let createdBy = items.twChannelObj?.createdBy
let dateCreated = items.twChannelObj?.dateCreated
let dateCreatedAsDate = items.twChannelObj?.dateCreatedAsDate
let dateUpdatedAsDate = items.twChannelObj?.dateUpdatedAsDate
let dateUpdated = items.twChannelObj?.dateUpdated
let friendlyName = items.twChannelObj?.friendlyName
let lastMessageDate = items.twChannelObj?.lastMessageDate
let lastMessageIndex = items.twChannelObj?.lastMessageIndex
let members = items.twChannelObj?.members
let messages = items.twChannelObj?.messages
let sid = items.twChannelObj?.sid
let status = items.twChannelObj?.status
let synchronizationStatus = items.twChannelObj?.synchronizationStatus
let type = items.twChannelObj?.type
var uniqueName = items.twChannelObj?.uniqueName
if uniqueName == nil
{
uniqueName = ""
}
let channelObject : ChannelObject = ChannelObject.init(createdBy: createdBy!, dateCreated: dateCreated!, dateCreatedAsDate: dateCreatedAsDate!, dateUpdatedAsDate: dateUpdatedAsDate!, dateUpdated: dateUpdated!, friendlyName: friendlyName!, lastMessageDate: lastMessageDate!, lastMessageIndex: lastMessageIndex!, members: members!, messages: messages!, sid: sid!, synchronizationStatus: synchronizationStatus!.rawValue, type: type!.rawValue, uniqueName: uniqueName!,status:status!.rawValue)
do
{
if #available(iOS 11.0, *) {
let dataMember = try NSKeyedArchiver.archivedData(withRootObject: channelObject, requiringSecureCoding: false)
user.setValue(dataMember, forKey: "twChannelObj")
} else {
// Fallback on earlier versions
}
}
catch
{
print("Error \(error)")
}
I am fetching data from the model in this way
if let birthdate = personInfo?.str_Birthdate {
cell.dobTF.text = birthdate
}
But app crash and return this error
'-[NSNull length]: unrecognized selector sent to instance 0x10f8c6fc0'
What you are getting here is NSNull. It is an object representing null in context of objective-C arrays and dictionaries. Specifically in JSONs it distinguishes between receiving field (null) instead of not receiving a field at all. In your case I assume this value is force-unwrapped as a string so the error is a bit late. Please try the following:
if let dateObject = personInfo?.str_Birthdate {
if let nullObject = dateObject as? NSNull {
// A field was returned but is (null)
} else if let stringObject = dateObject as? String {
cell.dobTF.text = stringObject
} else {
// Unknown object
}
} else {
// This field is missing
}
You can actually just convert all NSNull instances to nil using something like:
func removeNSNullInstancesFrom(_ item: Any?) -> Any? {
guard let item = item else { return nil }
if let _ = item as? NSNull {
return nil
} else if let array = item as? [Any] {
return array.compactMap { removeNSNullInstancesFrom($0) }
} else if let dictionary = item as? [String: Any] {
var newDict: [String: Any] = [String: Any]()
dictionary.forEach { item in
guard let value = removeNSNullInstancesFrom(item.value) else { return }
newDict[item.key] = value
}
return newDict
} else {
return item
}
}
You can use this on the whole response object or a specific item. In your case you can do: cell.dobTF.text = removeNSNullInstancesFrom(birthdate).
But this method should in general recursively remove all NSNull instances from fields, arrays and dictionaries which are standard for JSON.
I am using Realm and I have an extension that I use to convert my Realm model into a Dictionary , but I do not know how to convert all my Realm models at once. I want to know how do I convert all the realm Objects at once and in one place, so that I can send that dictionary to a API.
Here are my Realm Object Models and the extension I use:
class OrderItemList: Object {
dynamic var dateCreated = NSDate()
let orderItems = List<OrderItem>()
}
class OrderItem: Object {
dynamic var name = " "
dynamic var amount = 0
dynamic var internalUnique = Int()
dynamic var isCompleted = false
}
Extension:
extension Object {
func toDictionary() -> NSDictionary {
let properties = self.objectSchema.properties.map { $0.name }
let dictionary = self.dictionaryWithValuesForKeys(properties)
let mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeysWithDictionary(dictionary)
for prop in self.objectSchema.properties as [Property]! {
// find lists
if let nestedObject = self[prop.name] as? Object {
mutabledic.setValue(nestedObject.toDictionary(), forKey: prop.name)
} else if let nestedListObject = self[prop.name] as? ListBase {
var objects = [AnyObject]()
for index in 0..<nestedListObject._rlmArray.count {
let object = nestedListObject._rlmArray[index] as AnyObject
objects.append(object.toDictionary())
}
mutabledic.setObject(objects, forKey: prop.name)
}
}
return mutabledic
}
}
Unfortunately, there's no magic bullet for converting a batch of Realm objects to a dictionary. You'll need to query for the objects you want, and then loop through each one to produce a serialized version of it.
let realm = try! Realm()
var objectDictionaries = [NSDictionary]()
let allObjects = realm.objects(OrderItemList.self)
for object in allObjects {
let dictionary = object.toDictionary()
objectDictionaries.append(dictionary)
}
I hope that answered your question!
Why do I get an error when I used valueForKey... I am using same trick like in objectiveC ...
In ObjectiveC, the code is
self.strSubscribe =[responseObject[#"subscribe"] valueForKey:#"subscribe_ids"];
In Swift , the code is
self.strSubscribe = responseObject["subscribe"].valueForKey["subscribe_ids"] as! String
I declare the variables like
var arraySubCategory : NSMutableArray! = NSMutableArray()
var strSubscribe:String!
And I tried to access the value from below response
{
subscribe =
{
"subscribe_ids" = "1,14";
}
}
Edit
It works using Amit and Eric's solution but now for following data
{
data = (
{
"subscribe_ids" = "1,14";
}
);
}
let dictionary = responseObject["data"][0] as! Dictionary<String,AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
OR//
if let dic = responseObject["data"][0] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids
}
but it gives me error:
could not find member 'subscript'
Swift doesn't know the type of responseObject["subscribe"], you have to help the compiler a bit; for example:
if let dic = responseObject["subscribe"] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids // "1,14"
}
UPDATE:
It's still the same problem: the compiler doesn't know the type of responseObject["data"], so when you try to access the subscript there's an error (because you know it's a dictionary inside the array, but the compiler doesn't).
One solution is to give the type to the compiler by declaring an array of dictionaries in the if let condition:
if let arr = responseObject["data"] as? [[String:String]], let ids = arr[0]["subscribe_ids"] {
self.strSubscribe = ids
}
Notice that it's [[String:String]] (array of dictionaries), not [String:String] (dictionary).
Write like this.
let dictionary = responseObject["subscribe"] as! Dictionary<String, AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
Since responseObject["subscribe"] will give a AnyObject? output and AnyObject does not have any member called valueForKey.
My app parses podcast RSS feeds. I use 2 entities: Podcasts (to hold podcast-related data) and Episodes (Episodes data like summaries etc). After parsing a feed, I store the list of episodes in an Array called "episodesToDisplay". When a user subscribes to a podcast, I want to save the data held by that array in Core Data. Here is my code which throws an error on the annotated line below:
class Podcasts: UITableViewController {
var currentPodcast: Podcasts!
override func viewDidLoad() {
super.viewDidLoad()
let podcastsEntity = NSEntityDescription.entityForName("Podcasts", inManagedObjectContext: self.managedContext)
let podcastsFetch = NSFetchRequest(entityName: "Podcasts")
var error: NSError?
let result = self.managedContext.executeFetchRequest(podcastsFetch, error: &error) as [Podcasts]?
if let resu = result {
println("res is \(resu.count)")
self.currentPodcast = resu[0] as Podcasts
} else {
println("did not work")
}
}
#IBAction func subscribe(sender: AnyObject) {
for dict: AnyObject in episodesToDisplay {
let episodesEntity = NSEntityDescription.entityForName("Episodes", inManagedObjectContext: self.managedContext)
let episodesToSave = Episodes(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episodes = currentPodcast.episode.mutableCopy() as NSMutableOrderedSet
let btDict = dict as NSDictionary <---------------- Crash
episodesToSave.title = btDict["title"] as String
episodesToSave.summary = btDict["summary"] as String
episodesToSave.link = btDict["link"] as String
episodes.addObject(episodesToSave)
currentPodcast.episode = episodes.copy() as NSOrderedSet
}
// Save
var error:NSError?
if !self.managedContext.save(&error) {
println("could not save \(error)")
}
}
Any ideas please?
The error indicates that your array doesn't contain NSDictionary objects - that is why you get dynamic cast exception when you try and access an element as an NSDictionary.
From your comment it seems that your array actually contains MWFeedItem objects, so all you need to do is change your code to use that object type and then you can access the properties of the MWFeedItem -
#IBAction func subscribe(sender: AnyObject) {
for item: MWFeedItem in episodesToDisplay {
let episodesEntity = NSEntityDescription.entityForName("Episodes", inManagedObjectContext: self.managedContext)
let episodesToSave = Episodes(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episodes = currentPodcast.episode.mutableCopy() as NSMutableOrderedSet
episodesToSave.title = item.title
episodesToSave.summary = item.summary
episodesToSave.link = item.link
episodes.addObject(episodesToSave)
currentPodcast.episode = episodes.copy() as NSOrderedSet
}