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!
Related
I have my class defined as:
class Device: Object {
dynamic public var assetTag = ""
dynamic var location = ""
}
I also have two arrays defined as:
let array = ["12", "42", "52", "876"]
let array2 = ["SC", "EDS", "DS", "EF"]
I would like to loop through the first array and add each value to my realm Device.assetTag object and loop through my second array and add each value to the Device.location object.
I tried using the code from the Realm readme to just add data from the first array but it did not seem to loop:
let realmArray = Device(value: array)
let realm = try! Realm()
try! realm.write {
realm.add(realmArray)
}
You have two arrays one that holds asetTags and Another location so first you have to build the object from those. You can do something like following (probably refactoring needed)
class Device: Object {
dynamic public var assetTag = ""
dynamic var location = ""
}
class Test {
let assetTags = ["12", "42", "52", "876"]
let locations = ["SC", "EDS", "DS", "EF"]
func saveDevice() {
let realm = try! Realm()
try! realm.write {
let allDevices = getDeviceArray()
for device in allDevices {
realm.add(device)
}
}
}
func getDeviceArray() -> [Device] {
let requiredDevices = [Device]()
var index = 0
for tag in assetTags {
let locationForTag = locations[index]
let device = Device()
device.assetTag = tag
device.location = locationForTag
requiredDevices.append(device)
index += 1
}
return requiredDevices
}
}
Remember to put loop within realm.write for batch operation, this ensure the connection to write is made once.
I'm new to Swift. I have been having trouble downloading Firebase dictionaries and turning them into an array of objects.
What am I doing wrong with the syntax below? I've spent the last two days unsuccessfully trying to figure this out. The following gives me an index out of range error. Is this because the Firebase Dictionary hasn't finished downloading yet or is my for in loop sytax flawed? Perhaps both? Thanks.
// Array of Location Objects
var locationsArray:[Location] = [Location]()
var ref = Firebase(url: "<MYFIREBASEURL>")
var dictionaryOfRecommendations:[NSDictionary] = [NSDictionary]()
var currentlyConstructingLocation:Location = Location()
func getLocationData() {
let titleRef = self.ref.childByAppendingPath("events")
titleRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
var tempDict = [NSDictionary]()
for item in snapshot.children {
let child = item as! FDataSnapshot
let dict = child.value as! NSDictionary
tempDict.append(dict)
}
self.dictionaryOfRecommendations = tempDict
})
// Parse data from Firebase
// Loop through each dictionary and assign values to location object
var index:Int
for index in 0...dictionaryOfRecommendations.count {
// Current Json dictionary
let jsonDictionary:NSDictionary = self.dictionaryOfRecommendations[index]
self.currentlyConstructingLocation.title = jsonDictionary["title"] as! String!
self.currentlyConstructingLocation.locationsLatitudeArray = jsonDictionary["latitude"] as! Double
self.currentlyConstructingLocation.locationsLongitudeArray = jsonDictionary["longitude"] as! Double
// Append to Locations Array and start new Location
self.locationsArray.append(currentlyConstructingLocation)
self.currentlyConstructingLocation = Location()
}
// Notify the MainViewController that the Locations are ready.
...
}
Here's the updated correct code for the question above based on Jay's helpful guidance:
// Model to download location data for events.
//Firebase reference
var ref = Firebase(url: "<MYFIREBASEURL")
var locationsArray:[Location] = [Location]()
var dictionaryOfRecommendations:[NSDictionary] = [NSDictionary]()
var currentlyConstructingLocation:Location = Location()
func getLocationData() {
let titleRef = self.ref.childByAppendingPath("events")
titleRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
var tempDict = [NSDictionary]()
for item in snapshot.children {
let child = item as! FDataSnapshot
let dict = child.value as! NSDictionary
tempDict.append(dict)
}
self.dictionaryOfRecommendations = tempDict
self.ParseFirebaseData()
})
}
func ParseFirebaseData() {
// Parse data from Firebase
// Loop through each dictionary and assign values to location object
var index:Int
for index in 0...dictionaryOfRecommendations.count - 1 {
// Current Json dictionary
let jsonDictionary:NSDictionary = self.dictionaryOfRecommendations[index]
self.currentlyConstructingLocation.title = jsonDictionary["title"] as! String!
self.currentlyConstructingLocation.locationsLatitudeArray = jsonDictionary["latitude"] as! Double
self.currentlyConstructingLocation.locationsLongitudeArray = jsonDictionary["longitude"] as! Double
// Append to Locations Array and start new Location
self.locationsArray.append(currentlyConstructingLocation)
self.currentlyConstructingLocation = Location()
}
}
Need a little help please, I am reasonably new to the iOS developing and current stuck in a small problem.
I am trying to pass the values that I have got from JSON file into another method so I can generate the barGraph, but I am having problem to pass it as an String Array.
Here is the view controller class, I am trying to figure out a way to complete it, please also see the 2 classes that I have created, I can confirm the values are all there.
View Controller Class -
import UIKit
import Charts
class ViewController: UIViewController, ChartViewDelegate {
#IBOutlet weak var barChartView: BarChartView!
let model:JsonModel = JsonModel()
var data:[Data] = [Data]()
var censusYear:[String]! = []
var currentData:Data = Data()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
barChartView.delegate = self
// Kick off the data gathering
self.data = self.model.getData()
// Kick off the graph generation
setChart(<#T##[String]#>, values: <#T##[Double]#>)
}
func setChart(dataPoints: [String], values: [Double]) {
barChartView.noDataTextDescription = " You need to provide data for the chart"
var dataEntries: [BarChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(yVals: dataEntries, label: "Population")
let chartData = BarChartData(xVals: censusYear, dataSet: chartDataSet)
barChartView.data = chartData
barChartView.descriptionText = "This is a test"
chartDataSet.colors = ChartColorTemplates.colorful()
barChartView.animate(xAxisDuration: 2.0, yAxisDuration: 2.0, easingOption: .EaseInBounce)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Data Class -
class Data: NSObject {
var area:String = ""
var censusYear:String = ""
var sex:String = ""
var population:Double = 0
}
JSON Data Model Class -
class JsonModel: NSObject {
func getData() -> [Data] {
// Get array of data objects
var data:[Data] = [Data]()
// Get JSON array of dictionaries
let jsonObjects:[NSDictionary] = self.getLocalFile()
// Loop through each dictionary and assign values to the data object parse JSON File
var index:Int
for index = 0; index < jsonObjects.count; index++ {
// Current JSON Dict
let jsonDictionary:NSDictionary = jsonObjects[index]
// Create a Data object
let d:Data = Data()
// Assign value to each key value pair to the data object
d.area = jsonDictionary["area"] as! String
d.censusYear = jsonDictionary["census year"] as! String
d.sex = jsonDictionary["sex"] as! String
d.population = jsonDictionary["population"] as! Double
// Add the data into data array
data.append(d)
}
// Return list of data object
return data
}
func getLocalFile() -> [NSDictionary] {
// Get an NSURL object pointing to the JSON filed in the app bundle
let appBundlePath:String? = NSBundle.mainBundle().pathForResource("sample3", ofType: "json")
// TODO: Use optional binding or guard to check if path exisits
if let actualBundlePath = appBundlePath {
// Path exists
let urlPath:NSURL = NSURL(fileURLWithPath: actualBundlePath)
let jsonData:NSData? = NSData(contentsOfURL: urlPath)
if let actualJsonData = jsonData {
// NSData exists, use the NSJSONSerialization classes to parse the data and create the dictionaries
do {
let arrayOfDictionaries:[NSDictionary] = try NSJSONSerialization.JSONObjectWithData(actualJsonData, options: NSJSONReadingOptions.MutableContainers) as! [NSDictionary]
return arrayOfDictionaries
}
catch {
// There was an error parsing the json file
}
}
else {
// NSData doesnt exist
}
}
else {
// Path to json file in the app bundle doesnt exist
}
// Return an empty array
return [NSDictionary]()
}
Considering the following model:
class Person: Object {
dynamic var name = ""
let hobbies = Dictionary<String, String>()
}
I'm trying to stock in Realm an object of type [String:String] that I got from an Alamofire request but can't since hobbies has to to be defined through let according to RealmSwift Documentation since it is a List<T>/Dictionary<T,U> kind of type.
let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore
I also tried to redefine init() but always ended up with a fatal error or else.
How can I simply copy or initialize a Dictionary in RealSwift?
Am I missing something trivial here?
Dictionary is not supported as property type in Realm.
You'd need to introduce a new class, whose objects describe each a key-value-pair and to-many relationship to that as seen below:
class Person: Object {
dynamic var name = ""
let hobbies = List<Hobby>()
}
class Hobby: Object {
dynamic var name = ""
dynamic var descriptionText = ""
}
For deserialization, you'd need to map your dictionary structure in your JSON to Hobby objects and assign the key and value to the appropriate property.
I am currently emulating this by exposing an ignored Dictionary property on my model, backed by a private, persisted NSData which encapsulates a JSON representation of the dictionary:
class Model: Object {
private dynamic var dictionaryData: NSData?
var dictionary: [String: String] {
get {
guard let dictionaryData = dictionaryData else {
return [String: String]()
}
do {
let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
return dict!
} catch {
return [String: String]()
}
}
set {
do {
let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
dictionaryData = data
} catch {
dictionaryData = nil
}
}
}
override static func ignoredProperties() -> [String] {
return ["dictionary"]
}
}
It might not be the most efficient way but it allows me to keep using Unbox to quickly and easily map the incoming JSON data to my local Realm model.
I would save the dictionary as JSON string in Realm. Then retrive the JSON and convert to dictionary. Use below extensions.
extension String{
func dictionaryValue() -> [String: AnyObject]
{
if let data = self.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
return json!
} catch {
print("Error converting to JSON")
}
}
return NSDictionary() as! [String : AnyObject]
} }
and
extension NSDictionary{
func JsonString() -> String
{
do{
let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String.init(data: jsonData, encoding: .utf8)!
}
catch
{
return "error converting"
}
}
}
UPDATE 2021
Since Realm 10.8.0, it is possible to store a dictionary in a Realm object using the Map type.
Example from the official documentation:
class Dog: Object {
#objc dynamic var name = ""
#objc dynamic var currentCity = ""
// Map of city name -> favorite park in that city
let favoriteParksByCity = Map<String, String>()
}
Perhaps a little inefficient, but works for me (example dictionary from Int->String, analogous for your example):
class DictObj: Object {
var dict : [Int:String] {
get {
if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
else {
var ret : [Int:String] = [:];
Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
return ret;
}
}
set {
_keys.removeAll()
_values.removeAll()
_keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
_values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
}
}
var _keys = List<IntObj>();
var _values = List<StringObj>();
override static func ignoredProperties() -> [String] {
return ["dict"];
}
}
Realm can't store a List of Strings/Ints because these aren't objects, so make "fake objects":
class IntObj: Object {
dynamic var val : Int = 0;
}
class StringObj: Object {
dynamic var val : String = "";
}
Inspired by another answer here on stack overflow for storing arrays similarly (post is eluding me currently)...
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
}