Working with Realm and result JSON - ios

I am new to realm and I am trying to use Realm in my project. Here I am trying to parse JSON and save it using realm. When I am trying to loop through the result array error occurs
'Attempting to modify object outside of a write transaction - call beginwritetransaction on an RLMRealm instance first'
This is the JSON result:
{"data":[{"id":1,"parent_id":0,"name":"JenniferMaenle","title":"Ms","phone":"","address":"Toled, ohio","email":"jlmaenle#aol.com","image":"44381525_2017.jpg","relation_id":5,"created_at":null,"updated_at":"2017-08-10 02:30:05"},{"id":2, "parent_id":1,"name":"Khadeeja","title":"","phone":"","address":"","email":"","image":"Khadeeja_2017-07-17.jpg","relation_id":2,"created_at":null,"updated_at":"2017-07-17 08:3:12"}]}
I am trying to parse JSON and save it in the Realm database. Here is my try:
class Person: Object {
dynamic var name = ""
dynamic var title = ""
dynamic var address = ""
}
override func viewDidLoad() {
super.viewDidLoad()
self.add()
}
func add(){
guard let data = dataFromFile("ServerData") else { return }
let persons = Person()
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: AnyObject] {
if let data = json["data"] as? [[String:AnyObject]]{
for eachItem in data{
persons.name = eachItem["name"] as! String
persons.title = eachItem["title"] as! String
persons.address = eachItem["address"] as! String
try! realm.write {
realm.add(persons)
}
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
}

Do not modify the persons object and add it to the realm again. The error you see is because you add persons to the realm and then edit it on the second iteration in the for eachItem in data again. Persons is already added to the realm in this iteration and you try to give it a new name outside a realm write transaction. That's why you see the error. It's better to create a new object for every person.
func add(){
guard let data = dataFromFile("ServerData") else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: AnyObject] {
if let data = json["data"] as? [[String:AnyObject]]{
for eachItem in data{
let persons = Person()
persons.name = eachItem["name"] as! String
persons.title = eachItem["title"] as! String
persons.address = eachItem["address"] as! String
try! realm.write {
realm.add(persons)
}
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
}

As the error indicates, you need to preform the transaction inside a write,
One option is to write each person individually:
for eachItem in data{
let person = Person()
person.name = eachItem["name"] as! String
person.title = eachItem["title"] as! String
person.address = eachItem["address"] as! String
try! realm.write {
realm.add(person)
}
}
Second option is to build an object and then write:
for eachItem in data{
let person = Person();
person.name = eachItem["name"] as! String
person.title = eachItem["title"] as! String
person.address = eachItem["address"] as! String
persons.add(person)
}
try! realm.write {
realm. append(persons)
}
You might need to make few changes to this, but this is the idea.

Related

How can you get attribute names from an Entity from CoreData at an iOS app

I am reading data with following code from CoreData but instead of that can we read first attribute names "firstName", "lastName", "age" from CoreData into an array and read their values instead of writing all the names in code.
It is repeated work because they are written in DataModel as well.
loadData() {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result as! [NSManagedObject] {
fNames = data.value(forKey: "firstName") as! String
lNames = data.value(forKey: "lastName") as! String
age = data.value(forKey: "age") as! Int
print("first \(fNames), last : \(lNames), last : \(age)")
}
} catch {
print("Could not load data: \(error.localizedDescription)")
}
}
Use the class that Xcode has generated for you that has the same name as the entity name
loadData() {
//Declare fetch request to hold the class you want to fetch
let fetchRequest = NSFetchRequest<Entity>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result {
// result is now [Entity] so you can access properties directly
// and without casting
let firstName = data.firstName
let lastName = data.lastName
let age = data.age
print("first \(firstName), last : \(lastName), age : \(age)")
}
} catch let error as NSError {
print("Could not load data: \(error.localizedDescription)")
}
}
Try this, access your entity name from NSManagedObject
e.g.
For AppDelegate.SharedInstance(), just declare this func in AppDelegate.swift
class func sharedInstance() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"CallHistory") //Here CallHistory is my entity name which i can access as NSManagedObject
let arr_callhistory = try AppDelegate.sharedInstance().persistentContainer.viewContext.fetch(fetchRequest) as! [CallHistory]
if arr_callhistory.count != 0
{
for callhistory_dict in arr_callhistory
{
let callStatus = callhistory_dict.callStatus
}
}

Searchbar filter based on json

I'm having a name value to my tableviewcell like so..
for asd in orderDetails {
if let jsonStr = asd.value(forKey: "customerJson") as? String {
let data = jsonStr?.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any] {
for item in json {
if item.key == "first_Name" {
cell.nameLabel.text = item.value as? String //ASSIGNED HERE
}
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
}
Now I want to search on the search bar based on this name. While searching in other cases where the core data attributes were mentioned directly I did something like so which worked fine..
filtered = self.newProdDetails.filter({( data : NewProduct) -> Bool in
return (data.name?.lowercased().contains(searchText.lowercased()))! //Here the entity NewProduct has an attribute name
})
But in the current scenario, the attribute is a json string called customer_json given like so and I wanted to filter based on the first_name..
customer_json={
"mobile_number”:”9876543210”,
"first_name”:”testName”,
"last_name”:”testLastName”,
"seller_id":"93"
}
simply parse your JSON into the array or dictionary a/c to json with help :
let arrayJson = JSONSerialization.jsonObject(with:....
.....
then apply Search simply on an array like this :
let filterArr = arrayJson.filter {
return (($0["first_name"] as! String).lowercased().contains(searchText.lowercased()))
}

Swift JSON parsing and printing a specified value from an array

Hi I'm trying to get data from a certain JSON API. I can gat a snapshot of all values from the API, which is shown below. But I can't manage to put a specifiek row in a variable. This is the JSON form which I get. I want to print the "Description" value.Can someone help me with this?
And Hier is my code:
func apiRequest() {
let config = URLSessionConfiguration.default
let username = "F44C3FC2-91AF-5FB2-8B3F-70397C0D447D"
let password = "G23#rE9t1#"
let loginString = String(format: "%#:%#", username, password)
let userPasswordData = loginString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData?.base64EncodedString()
let authString = "Basic " + (base64EncodedCredential)!
print(authString)
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
var running = false
let url = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session.dataTask(with: url! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let array = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(array)
if let items = array["items"] {
if let description = items["Description"] as? [[String:Any]]{
print(description as Any)
}
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task.resume()
while running {
print("waiting...")
sleep(1)
}
}
First of all the array is not an array and not AnyObject, it's a dictionary which is [String:Any] in Swift 3.
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
I don't know why all tutorials suggest .mutableContainers as option. That might be useful in Objective-C but is completely meaningless in Swift. Omit the parameter.
The object for key itemsis an array of dictionaries (again, the unspecified JSON type in Swift 3 is Any). Use a repeat loop to get all description values and you have to downcast all values of a dictionary from Any to the expected type.
if let items = dictionary["items"] as? [[String:Any]] {
for item in items {
if let description = item["Description"] as? String {
print(description)
}
}
}
Looks like items is an array that needs to be looped through. Here is some sample code, but I want to warn you that this code is not tested for your data.
if let items = array["items"] as? [[String: AnyObject]] {
for item in items {
if let description = item["Description"] as? String{
print("Description: \(description)")
}
}
}
This code above, or some variation of it, should get you on the right track.
use the SwiftyJSON and it would be as easy as json["items"][i].arrayValue as return and array with items Values or json["items"][i]["description"].stringValue to get a string from a row

swift: Alphabetiz data being parsed from a JSON

I wrote my own function in Swift2 to parse a JSON. Once the JSON is parsed, a list of data that was pulled from the JSON is displayed in a tableView on my app. I am trying to figure out how to display this data in alphabetical order. I think this needs to happen somewhere before the append method I call in the function. I would imagine this needs to be a sort function but I have not been able to figure out the correct sort function in Swift2 that will execute this properly. Any help I can get is appreciated!
Here is my parseJSON function:
func parseJSON(){
do{
let data = NSData(contentsOfURL: NSURL(string: "https://jsonblob.com/api/jsonBlob/580d0ccce4b0bcac9f837fbe")!)
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
for anItem in jsonResult as! [Dictionary<String, AnyObject>]{
let mifiName2 = anItem["name"] as! String
let mifiId = anItem["employeeId"] as! Int
let newName = Name(mifiName: mifiName2, mifiId: mifiId)
nameOfMifi.append(newName)
//print("Name: \(newName)")
}
}
catch let error as NSError{
print(error.debugDescription)
}
}
You need to sort your array after all the object is append in Array means after the for loop.
for anItem in jsonResult as! [Dictionary<String, AnyObject>]{
let mifiName2 = anItem["name"] as! String
let mifiId = anItem["employeeId"] as! Int
let newName = Name(mifiName: mifiName2, mifiId: mifiId)
nameOfMifi.append(newName)
//print("Name: \(newName)")
}
//Now you need to sort your array on the basis of name like this
nameOfMifi.sortInPlace { $0.mifiName < $1.mifiName }
Edit: As #vadian suggested do not use NSData(contentsOfURL:) because it will block your UI, so batter to use NSURLSession like this.
let session = NSURLSession.sharedSession()
let url = NSURL(string: "https://jsonblob.com/api/jsonBlob/580d0ccce4b0bcac9f837fbe")!
var task = session.dataTaskWithURL(url, completionHandler: {
(data, response, error) -> Void in
if error != nil {
return
}
if let jsonResult = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? [Dictionary<String, AnyObject>] {
for anItem in jsonResult {
let mifiName2 = anItem["name"] as! String
let mifiId = anItem["employeeId"] as! Int
let newName = Name(mifiName: mifiName2, mifiId: mifiId)
nameOfMifi.append(newName)
//print("Name: \(newName)")
}
//Now you need to sort your array on the basis of name like this
nameOfMifi.sortInPlace { $0.mifiName < $1.mifiName }
//Now reload tableView on main thread.
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
})
task.resume()

Segmentation Fault while reading JSON parse to NSArray in swift

There is a method used to fill realm database from json:
func parseJSON(data: NSData) -> NSArray? {
do {
let array: NSArray = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as! NSArray
return array
} catch _ {
return nil
}
}
//parameter came from previous method
func updateDatabaseFromParsedJson(parsedJson: NSArray) {
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
for i in 0..<parsedJson.count {
let deviceObject = parsedJson[i]
let name = deviceObject["name"] as! String
let id = deviceObject["id"] as! Int
var device = Device()
device.name = name
device.id = id
try! realm.write {
realm.add(device)
}
var deviceMeasuresArray = deviceObject["measures"] as! NSArray
for i in 0..<deviceMeasuresArray.count {
var measureObject = deviceMeasuresArray[i]
var measure = Measure()
measure.slug = measureObject["name"]
measure.device = device
measure.localize()
try! realm.write {
realm.add(measure)
}
var measureEntriesArray = measureObject["averages"] as! NSArray
for i in 0..<measureEntriesArray.count {
var entryObject = measureEntriesArray[i]
var entry = PeriodAverage()
entry.measure = measure
entry.value = entryObject["value"]
entry.start = NSDate.parse(entryObject["start"])
entry.end = NSDate.parse(entryObject["end"])
entry.length = entryObject["length"]
try! realm.write {
realm.add(entry)
}
}
}
}
}
extension NSDate {
class func parse(dateString: String) -> NSDate {
let format = "yyyy-MM-dd'T'HH:mm:ss'Z'"
let formatter = NSDateFormatter()
formatter.dateFormat = format
return formatter.dateFromString(dateString)!
}
}
JSON itself http://188.166.51.200/api/v1/actual_data/
While compiling I get error Error:unable to execute command: Segmentation fault: 11
Where I am wrong and how to properly parse my json? I think problem is in lines where json fields forcely parsed to objects but I am new in swift and can't exactly determine the error.
You've apparently run across a compiler issue (which you should report to Apple), although it's easy enough to work around. If you're using Swift you should really be using Swift collection types instead Foundation collection types if possible (i.e., Array instead of NSArray) which allow for more type information. Also, while you're casting some of the values you're getting out of your JSON, you're not casting them all. Adding this additional type information will make the compiler behave and work around the issue. I would suggest the following edit:
func updateDatabaseFromParsedJson(parsedJson: Array<AnyObject>) {
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
for i in 0..<parsedJson.count {
let deviceObject = parsedJson[i] as! Dictionary<String, AnyObject>
let name = deviceObject["name"] as! String
let id = deviceObject["id"] as! Int
var device = Device()
device.name = name
device.id = id
try! realm.write {
realm.add(device)
}
var deviceMeasuresArray = deviceObject["measures"] as! Array<AnyObject>
for i in 0..<deviceMeasuresArray.count {
var measureObject = deviceMeasuresArray[i] as! Dictionary<String, AnyObject>
var measure = Measure()
measure.slug = measureObject["name"] as! String // I'm guessing on the type here
measure.device = device
measure.localize()
try! realm.write {
realm.add(measure)
}
var measureEntriesArray = measureObject["averages"] as! Array<AnyObject>
for i in 0..<measureEntriesArray.count {
var entryObject = measureEntriesArray[i] as! Dictionary<String, AnyObject>
var entry = PeriodAverage()
entry.measure = measure
entry.value = entryObject["value"] as! String // Guessing on the type here also
entry.start = NSDate.parse(entryObject["start"] as! String)
entry.end = NSDate.parse(entryObject["end"] as! String)
entry.length = entryObject["length"] as! String // Again, guessing on the type here
try! realm.write {
realm.add(entry)
}
}
}
}
}
Unrelated to the compiler issue, you could also use for-in loops to make your code more Swift like. You can read more about them in the For-In Loops section of the Control Flow chapter of The Swift Programming Language.

Resources