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()))
}
Related
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.
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
I'm looking to try and reference all "titles" within this json (link here) in xcode 8. The issue is there's an object and array that need to be referenced (i believe) before I can pull the title data, and I'm not sure how to do that.
So far this is what i've got:
func fetchFeed(){
let urlRequest = URLRequest(url: URL(string: "http://itunes.apple.com/us/rss/topalbums/limit=10/json")!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error)
return
}
self.artists = [Artist]()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let feedFromJson = json["feed"]?["entry"] as? [[String : AnyObject]] {
for feedFromJson in feedsFromJson {
let feed = Feed()
if let entry = feedFromJson["entry"] as? String, let author = feedFromJson["domain"] as? String {
feed.entry = entry
article.headline = title
}
self.articles?.append(article)
}
}
DispatchQueue.main.async {
self.tableview.reloadData()
And thank you for your help in advance!
I'm working hard to try to understand what you need. If you want to get an Article array where the headline is the title label for the entry, here is how I cheated it out.
func articles(from json: Any?) -> [Article] {
guard let json = json as? NSDictionary, let entries = json.value(forKeyPath: "feed.entry") as? [NSDictionary] else {
return []
}
return entries.flatMap { entry in
guard let title = entry.value(forKeyPath: "title.label") as? String else {
return nil
}
var article = Article()
article.headline = title
return article
}
}
you call it as such
self.articles = articles(from: json)
NSDictionary has the method value(forKeyPath:) that is near magic. Calling json.value(forKeyPath: "feed.entry") returns an array of dictionaries. Each dictionary is an "entry" object in the json. Next, I map each entry to call entry.value(forKeyPath: "title.label") which returns a string.
If this is something more than a quick solution, then I would consider adding SwiftyJSON to your project.
func articles(from json: Any?) -> [Article] {
return JSON(json)["feed"]["entry"].arrayValue.flatMap { entry in
guard let title = entry["title"]["label"].string else {
return nil
}
var article = Article()
article.headline = title
return article
}
}
There is two kinds of titles.
the "feed" and the "entry".
if let entry = feedFromJson["entry"] as? String, let author = feedFromJson["domain"] as? String {
The practice of iOS is not this.
feedFromJson["entry"] is nil ,not a string . I guess you try to get the "feed" title.
if let entry = (json["feed"] as Dictionary)?["title"]
To get the "entry" title. Just traverse the array, and get the title.
let titleDict = feedFromJson["title"] as? Dictionary
let title = titleDict["title"] as? String
article.headline = title
Better to know the structure of the JSON data.
It's too quick.
if let feedFromJson = json["feed"]?["entry"] as? [[String :
AnyObject]] {
You should step by step.
if let feedFromJson = (json["feed"] as Dictionary)?["entry"] as? [[String : AnyObject]] {
JSON :
{
"11/08/22":[
{
"Bill Gates":"Microsoft",
"Steve Balmer":"Microsoft"
}],
"13/08/22":[
{
"Tim Cook":"Apple",
"Jony Ive":"Apple"
}]
}
Swift Code :
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
//Get Result into Seperate Arrays
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 as? String }
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Requirements :
If i pass from program "11/08/22", I should be able to get all keys and values in the form of Array of String of only that array named "11/08/22" .
Better Explanation :
It should go into 11/08/22 and it should retrieve "Bill Gates","Steve Balmer" as Keys and "Microsoft" As a value in two separate arrays
For this example let's use an array to collect the people and a set to collect the companies:
var people: [String] = []
var companies: Set<String> = []
Subscript to the JSON dictionary with your "11/08/22" key and cast the result as an array of dictionaries.
Loop over this array and in the loop, add the keys to the people array and insert the values in the companies set.
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
if let dateContent = jsonResult["11/08/22"] as? [[String:String]] {
for group in dateContent {
people.appendContentsOf(group.keys)
group.values.forEach { companies.insert($0) }
}
}
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
print(people)
print(companies)
Result:
["Steve Balmer", "Bill Gates"]
["Microsoft"]
let keys=jsonResult["11/08/22"]?.allKeys as? [String];
let values=jsonResult["11/08/22"]?.allValues as? [String];
It was as simple as this
While fetching data from api I can get response either array of products or dictionary with error for e.g.
If everything went right api sends array of products as:
[
"Product1":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
},
"Product2":
{
name = "someting",
price = 100,
discount = 10%,
images = [image1,image2]
}
]
But if some error occur it sends dictionary with error message and code as:
{
error_message = "message"
error_code = 202
}
I am using this code to convert JSON data to array:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray{
//Some code....
} catch let error as NSError {
print("JSON Error: \(error.localizedDescription)")
}
but if I get error as dictionary it crash.
Problems:
1. How to know whether received data is an array or dictionary ?
2. Some time even key or value can be missing so checking for value it becomes very lengthy code like:
if let productsArray = jsonObject as? NSArray{
if let product1 = productsArray[0] as? NSDictionary{
if let imagesArray = product1["image"] as? NSArray{
if let imageUrl = imagesArray[0] as? String{
//Code ....
}
}
}
}
I read about guard keyword to reduce if condition but I don't have clear idea how to use here.
Problem 1:
For try catch , add an if let for casting the object as NSDictionary or NSArray like :
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let jsonDict = jsonObject as? NSDictionary {
// Do smthg.
}
if let jsonArray = jsonObject as? NSArray {
// Do smthg.
}
}catch {
//...
}
For Problem 2:
I think guard won't help you . It needs smthg like return / break in its else statement. If you don't want to throw your methods if one of your values isn't available you have to use this lengthy if let code style.
Maybe in your case best practice would be setting up a Data Model for Product with optional properties.
Class product {
var name:String?
var image:[NSData]? // maybe UIImage or smthg.
var price:Int?
var discount:Int?
init(jsonDic:NSDictionary){
// if it's not there it would be nil
self.name = jsonDic["name"] as? String
self.image = jsonDic["image"] as? NSArray
self.discount = jsonDic["discount"] as? Int
self.price = jsonDic["price"] as? Int
}
}
Now you can load those models with your data without the if let etc..
But if you wanna read those values you have to use the if let for checkin if its not nil.
For init in your case it should be something like this:
Add this into the if let statement of the do catch block ( ... as? NSArray // DO smthg. )
for item in jsonArray {
guard let jsonDic = item as? NSDictionary else { return }
// if you dont know every key you can just iterate through this dictionary
for (_,value) in jsonDic {
guard let jsonDicValues = value as? NSDictionary else { return }
productArray.append(Product(jsonDic: jsonDicValues)
}
}
As i said , know you got the whole if let stuff when reading from the model an not when writing ( reading the json )
You have a few things going on here, one, I would analyze your server's http response status code and only attempt to process data if you received a status code indicating you will have good data
// In practical scenarios, this may be a range
if statusCode != 200 {
// Handle a scenario where you don't have good data.
return
}
Secondly, I'd guard against the response, it looks like you have named it "data" like so:
guard let receivedData = data else {
return
}
From this point on, you can use the receivedData constant.
Here'd I'd attempt to use NSJSONSeralization, like you do, but by casting it into a Swift dictionary, like so:
if let responseDictionary = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? [String:AnyObject] {
// Here you can try to access keys on the response
// You can try things like
let products = responseDictionary?["products"] as? [[String:AnyObject]]
for product in products {
let productName = product["name"] as? String
if productName == nil {
continue
}
let newProduct = Product(name: productName)
// Do something with newly processed data
}
}
I tried to be general and also show you a guard example.
first of all I recommend using SwiftyJSON pod or the class straight into your Xcode, it works like a charm and you won't need to cast things down to figure whether you have a string or a dictionary or whatever. It is gold.
Once you've got your JSON, you can use this recursive function I created that does exactly what you need. It turns any Json into a dictionary. I mostly use it to save data into Firebase, without having to parse everything.
After you have imported SwiftyJSON into your project and added import SwiftyJSON to your Swift file you can:
//JSON is created using the awesome SwiftyJSON pod
func json2dic(_ j: JSON) -> [String:AnyObject] {
var post = [String:AnyObject]()
for (key, object) in j {
post[key] = object.stringValue as AnyObject
if object.stringValue == "" {
post[key] = json2dic(object) as AnyObject
}
}
return post
}
let json = JSON(value) // Value is the json structure you received from API.
var myDictionary = [String:AnyObject]()
myDictionary = json2dic(json)
You can catch the class of your response. If your response is kind of class dictionary, assign it as dictionary else if your response is kind of class array, assign it to array. Good luck.