swift3 parsing with model issue - ios

Hello I am trying to parse through this json file: http://pastebin.com/TCdkJnvZ
Here is the class I made of the information I want to parse out:
public class Recipe: NSObject {
var recipeID : NSNumber?
var categoryName : String?
var ingredients : [Int : Ingredients]?
var nutrition : [Nutrition]?
var imageName : String?
var instructions : [Int : String]?
}
class Ingredients : NSObject {
var id : NSNumber?
var name : String?
var quantity: NSNumber?
var unit : String?
}
class Nutrition : NSObject {
var serving : String?
var calories : NSNumber?
var fat : String?
var carbs : NSNumber?
}
This image is the current issue.. I am really not sure what I am doing wrong here.. so if I can get any help on fixing my logic/issue it would be appreciated.
func parseToJSON(data: Any) {
// add meals to here
var recipes : [Recipe]
// single meals here
var meals : Recipe
do {
if let json = try JSONSerialization.jsonObject(with: data as! Data) as? [String: Any],
meals.recipeID == json["recipeID"] as! NSNumber? ,
meals.imageName == json["ImageURL"] as! String?,
//meals.instructions == meals.parseInstructions(instructions: (json["Instructions"] as! String)),
meals.categoryName == "Meals" ,
let ingredients = json["Ingredients"] as! [[String: Any]]? {
for items in ingredients {
var i : Int = 0
var groceryItems : Ingredients
groceryItems.id = items["IngredientID"] as? NSNumber
groceryItems.name = items["Name"] as? String
groceryItems.quantity = items["Quantity"] as? NSNumber
groceryItems.unit = items["Unit"] as? String
meals.ingredients?[i] = groceryItems
}
};
let nutritionInfo = json["NutritionInfo"] as! [[String: Any]]? {
for items in nutritionInfo {
var nutrition : Nutrition
nutrition.serving = items["SingularYieldUnit"] as? String
nutrition.calories = items["TotalCalories"] as? NSNumber
nutrition.fat = items["TotalFat"] as? String
nutrition.carbs = items["TotalCarbs"] as NSNumber
meals.nutrition = nutrition
}
};
}
catch{
}
}

It looks like you have a variety of syntax errors, but the compiler can only show one issue at a time. I've cleaned up the code slightly for you, which should push you in the right direction. I can't completely fix it because I don't know what your exact intentions are.
Here is the updated parseToJSON function:
func parseToJSON(data: Any) {
let meals = Recipe()
do {
if let json = try JSONSerialization.jsonObject(with: data as! Data) as? [String: Any] {
meals.recipeID == json["recipeID"] as! NSNumber?
meals.imageName == json["ImageURL"] as! String?
//meals.instructions == meals.parseInstructions(instructions: (json["Instructions"] as! String)),
meals.categoryName == "Meals"
if let ingredients = json["Ingredients"] as! [[String: Any]]? {
for items in ingredients {
let groceryItems = Ingredients()
groceryItems.id = items["IngredientID"] as? NSNumber
groceryItems.name = items["Name"] as? String
groceryItems.quantity = items["Quantity"] as? NSNumber
groceryItems.unit = items["Unit"] as? String
meals.ingredients?.append(groceryItems)
}
}
if let nutritionInfo = json["NutritionInfo"] as! [[String: Any]]? {
for items in nutritionInfo {
let nutrition = Nutrition()
nutrition.serving = items["SingularYieldUnit"] as? String
nutrition.calories = items["TotalCalories"] as? NSNumber
nutrition.fat = items["TotalFat"] as? String
nutrition.carbs = items["TotalCarbs"] as? NSNumber
meals.nutrition?.append(nutrition)
}
}
}
}
catch{
}
}
I also changed the Recipe object's ingredients property to:
var ingredients : [Ingredients]?
The main issue was that a lot of your code was inside of an if let expression and your indentation was off so you couldn't as easily tell.

Related

I can't get the data from firebase database as a subclass object

My firebase data is as follows:
Matches
items
platinium
standard
-LQTnujHvgKsnW03Qa5s
code: "111"
date: "27/11/2018"
predictions
-0
prediction: "Maç Sonucu 1"
predictionRatio: "2"
startTime: "01:01"
I read this with the following code
databaseHandle = ref.observe(.childAdded, with: { (snapshot) in
if let matchDict = snapshot.value as? Dictionary<String, AnyObject> {
let m_key = snapshot.key
let m = Match(matchKey: m_key, matchData: matchDict)
self.matches.append(m)
}
self.matchesTableView.reloadData()
})
I have two datamodels
1 is match
2 is prediction
I can read code, date and starttime from database but with match object prediction data is not coming it says its nil, How can I get that data with match object?
You can set up the Model class as follows
class ListModel: NSObject {
var UID:String?
var Code:String?
var Date:String?
var PredictionsArr:[PredictionsObj]?
var StartTime:String?
}
class PredictionsObj : NSObject {
var Prediction : String?
var PredictionRatio : String?
}
In your ViewController you can add the below code
var ListArr = [ListModel]()
let ref = Database.database().reference().child("Matches").child(“items”).child(“standard”)
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let Obj = ListModel()
Obj.UID = snapshot.key
Obj.Code = dictionary["code"] as? String
Obj.Date = dictionary["date"] as? String
Obj.StartTime = dictionary["startTime"] as? String
let myPredictionsArr = dictionary["predictions"] as? NSArray
var myPredictionsObj = [PredictionsObj]()
if myPredictionsArr != nil{
for dict in myPredictionsArr as! [[String: AnyObject]] {
let detail = PredictionsObj()
detail.Prediction = dict["prediction"] as? String
detail.PredictionRatio = dict["predictionRatio"] as? String
myPredictionsObj.append(detail)
}
}
Obj.PredictionsArr = myPredictionsObj
self.ListArr.append(Obj)
self.ListTV.delegate = self
self.ListTV.dataSource = self
self.ListTV.reloadData()
}, withCancel: nil)

sort contents of 4 different arrays into same order

I have 4 arrays:
self.packName.append(object["packName"] as! String)
self.packDescription.append(object["packDesctription"] as! String)
self.packTitle.append(object["packTitle"] as! String)
let image = object.value(forKey: "file") as? PFFile
self.packImage.append(image!)
At the moment they are created and everything lines up (i.e., the packName matches the packImage. however if i try and sort them
self.packName = self.packName.sorted(by: { $0 < $1 })
self.packDescription = self.packDescription.sorted(by: { $0 < $1 })
self.packTitle = self.packTitle.sorted(by: { $0 < $1 })
self.packImage = self.packImage.sorted(by: { $0.name < $1.name })
they come out all mishmatch because this is sorting each array alphabetically.
How can i sort all 4 the same?
This sorted it. thanks for the tip guys
struct packStruct {
var packName : String
var packDescription : String
var packTitle : String
var packImage : PFFile
}
var packArray = [packStruct]()
var arrayName : String = ""
var arrayDescription : String = ""
var arrayTitle : String = ""
var arrayImage : PFFile
for object in packs {
self.arrayName = object.object(forKey: "packName") as! String
self.arrayDescription = object.object(forKey: "packDesctription") as! String
self.arrayTitle = object.object(forKey: "packTitle") as! String
self.arrayImage = object.object(forKey: "file") as! PFFile
self.packArray.append(packStruct(packName: self.arrayName, packDescription: self.arrayDescription, packTitle: self.arrayTitle, packImage: self.arrayImage))
}
// returns the array with all the data
print(packArray)
and get the data from the array
self.packArray[indexPath.item].packDescription
and can sort:
self.packArray = self.packArray.sorted{ $0.packName < $1.packName }

Firebase Retrieve Data - Could not cast value

First, I have checked these answers that do not help me :
Swift JSON error, Could not cast value of type '__NSArrayM' (0x507b58) to 'NSDictionary' (0x507d74)
Get data from Firebase
When retrieving data from Firebase (3.x), I have an error that occurs which is :
Could not cast value of type '__NSArrayM' (0x10ca9fc30) to 'NSDictionary' (0x10caa0108).
with this code and tree :
Tree :
Retrieving function :
func retrievePlanes() {
print("Retrieve Planes")
ref = FIRDatabase.database().reference(withPath: "results")
ref.observe(.value, with: { snapshot in
var newItems: [Planes] = []
for item in snapshot.children {
let planesItem = Planes(snapshot: item as! FIRDataSnapshot)
newItems.append(planesItem)
}
self.planes = newItems
self.tableView.reloadData()
})
}
Planes.swift - To manage the data
import Foundation
import Firebase
import FirebaseDatabase
struct Planes {
let key: String!
let name: String!
let code:String!
let flightRange: Int?
let typicalSeats: Int?
let maxSeats: Int?
let wingSpan: String!
let takeoffLength: Int?
let rateClimb: Int?
let maxCruiseAltitude: Int?
let cruiseSpeed: String!
let landingLength: Int?
let engines: String!
let votes: Int?
let data: String!
let imagePlane:String!
let imageTakenFrom: String!
let ref: FIRDatabaseReference?
init(name: String, code: String, flightRange: Int, typicalSeats: Int, maxSeats: Int, wingSpan: String, takeoffLength: Int, rateClimb: Int, maxCruiseAltitude: Int, cruiseSpeed: String, landingLength: Int, engines: String, votes: Int, data: String, imagePlane: String, imageTakenFrom: String, key: String = "") {
self.key = key
self.name = name
self.code = code
self.flightRange = flightRange
self.typicalSeats = typicalSeats
self.maxSeats = maxSeats
self.wingSpan = wingSpan
self.takeoffLength = takeoffLength
self.rateClimb = rateClimb
self.maxCruiseAltitude = maxCruiseAltitude
self.cruiseSpeed = cruiseSpeed
self.landingLength = landingLength
self.engines = engines
self.votes = votes
self.data = data
self.imagePlane = imagePlane
self.imageTakenFrom = imageTakenFrom
self.ref = nil
}
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
key = snapshot.key
let snapshotValue = snapshot.value as! [String:AnyObject]
name = snapshotValue["name"] as! String
code = snapshotValue["code"] as! String
flightRange = snapshotValue["intFlightRange"] as? Int
typicalSeats = snapshotValue["intTypicalSeats"] as? Int
maxSeats = snapshotValue["intMaxSeats"] as? Int
wingSpan = snapshotValue["wingSpan"] as! String
takeoffLength = snapshotValue["intTakeoffLength"] as? Int
rateClimb = snapshotValue["intRateClimb"] as? Int
maxCruiseAltitude = snapshotValue["intMaxCruiseAltitude"] as? Int
cruiseSpeed = snapshotValue["cruiseSpeed"] as! String
landingLength = snapshotValue["intLandingLength"] as? Int
engines = snapshotValue["engines"] as! String
votes = snapshotValue["votes"] as? Int
data = snapshotValue["data"] as! String
imagePlane = snapshotValue["planeImage"] as! String
imageTakenFrom = snapshotValue["imageTakenFrom"] as! String
}
on the line : let snapshotValue = snapshot.value as! [String:AnyObject]
I suppose that is due to the snapshot value that can't be retrieved under [String:AnyObject] because of the Int below.
(It is working when I only have String in another case).
I also know that Firebase "converts" the JSON tree to these objects [link]:
NSString
NSNumber
NSArray
NSDictionnary
but I can't figure out what has to be changed in the snapshot.value line to make it work.
Thanks for your help.
EDIT : I just sent a troubleshooting request. Will post updates.
EDIT 2: See Jay's answer. In my case the tree was wrong.
I took your code and shrunk it down a bit for testing, and it's working. (note Firebase 2.x on OS X and Swift 3 but the code is similar)
Firebase structure:
"what-am" : {
"results" : [ {
"code" : "738/B738",
"data" : "Boeing",
"engines" : "Rolls"
}, {
"code" : "727/B727",
"data" : "Boeing",
"engines" : "Pratt"
} ]
}
Here's the Planes struct
struct Planes {
var code:String!
var data: String!
var engines: String!
init(code: String, data: String, engines: String ) {
self.code = code
self.data = data
self.engines = engines
}
init(snapshot: FDataSnapshot) {
let snapshotValue = snapshot.value as! [String:AnyObject]
code = snapshotValue["code"] as! String
data = snapshotValue["data"] as! String
engines = snapshotValue["engines"] as! String
}
}
and then the code that reads in two planes, populates and array and then prints the array.
let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!
ref.observe(.value, with: { snapshot in
if ( snapshot!.value is NSNull ) {
print("not found")
} else {
var newItems: [Planes] = []
for item in (snapshot?.children)! {
let planesItem = Planes(snapshot: item as! FDataSnapshot)
newItems.append(planesItem)
}
self.planes = newItems
print(self.planes)
}
})
and finally the output
[Swift_Firebase_Test.Planes(code: 738/B738, data: Boeing, engines: Rolls),
Swift_Firebase_Test.Planes(code: 727/B727, data: Boeing, engines: Pratt)]
Key and name are nil as I removed then from the Planes structure.
The line you asked about
let snapshotValue = snapshot.value as! [String:AnyObject]
is valid as the snapshot contains a series of key:value pairs so String:AnyObject works.
This line changed due to Swift 3
for item in (snapshot?.children)!
but other than that, the code works.
Try this to ensure you are reading the correct node. This reads the above structure and prints out each engine type. (tested and works)
let ref = self.myRootRef.child(byAppendingPath: "what-am/results")!
ref.observe(.value, with: { snapshot in
if ( snapshot!.value is NSNull ) {
print("not found")
} else {
for child in (snapshot?.children)! {
let snap = child as! FDataSnapshot
let dict = snap.value as! [String: String]
let engines = dict["engines"]
print(engines!)
}
}
})
I think you are having an extra array in your results key-value on the firebase data.
You should try removing that array or
You may retrieve dictionary from first index of the array like;
// .. your code
let snapshotValue = (snapshot.value as! [AnyObject])[0] as! [String:AnyObject];
// .. your code
In your struct class make sure of these things:-
Avoid declaring your variables as :Int? because that's practically nil, change them to :Int!
Your key in your firebase is an Int and you are declaring your key in struct as let key: String!, Change it to let key: Int!
Prefer your snapshot dictionary declaration as let snapshotValue = snapshot.value as! [AnyHashable:Any] (as per swift 3)
Then your init function to :-
Just change the line
let snapshotValue = snapshot.value as! [String:AnyObject]
To
let snapshotValue = (snapshot.value as! NSArray)[0] as! [String:AnyObject]
update FIRDataSnapshot to DataSnapshot Swift 4
Below is an example for Swift 4. Where you need to change FIRDataSnapshot to DataSnapshot
func fetchChats(chatId: String) {
ref.child("chats").child("SomeChildId").observe(.value) { (snapshot) in
for child in snapshot.children {
let data = child as! DataSnapshot //<--- Update this line
let dict = data.value as! [String: AnyObject]
let message = dict["message"]
print(message!)
}
}
}

Making a Dictionary in Swift

I have an object taken from Parse and I want to save its columns into a Dictionary or something else (if it's better).
I want to have a Dictionary like this: ["name" : "Mike", "lastname" : "vorisis", "id" : "advsas"]
Below is the code I use to take my results:
func queryEvents() {
let query = PFQuery(className: "eventController")
query.limit = 1000
query.includeKey("idEvent")
query.includeKey("eventType")
query.includeKey("idEvent.idMagazi")
query.findObjectsInBackgroundWithBlock { (objects, error)-> Void in
if let objects = objects {
for object in objects {
var post = object["idEvent"] as? PFObject
var post2 = post!["idMagazi"]
print("retrieved related post: \(post2["name"]!)")
}
}
}
}
Something else (if it's better) is a custom class.
Change the type of idMagazi to the real type.
class Event {
let post : PFObject
let name : String
let idMagazi : String
init(object : PFObject) {
self.post = object
self.name = object["name"] as! String
self.idMagazi = object["idMagazi"] as! String
}
}
And use it
...
if let objects = objects as? [PFObject] {
var events = [Event]()
for object in objects {
let post = Event(object: object)
events.append(post)
print("retrieved related post: \(post.name)")
}
}
...
In Swift Dictionary<T,V> is equivalent to [T: V]. Type is inferred if not explicitly declared.
Empty dictionary creation (all equivalent):
var dict1: Dictionary<String, String> = [:]
var dict2: [String: String] = [:]
var dict3 = Dictionary<String, String>()
var dict4 = [String: String]()
Dictionary with values (all equivalent):
var dict5 = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict6: Dictionary<String, String> = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
var dict7: [String: String] = ["Foo": "Bar", "Foo1": "Bar1", "Foo2": "Bar2"]
Add values to an existing dictionary:
dict["Foo"] = "Bar"
In your specific scenario, you could use this:
let dict = ["name" : name, "lastname" : lastname , "id" : id]
where name, lastname and id are String variables.
Update based on your own answer:
Having this struct:
struct Event {
var nameEvent: String
var nameMagazi: String
}
You can use this approach, that avoid having an external index and uses an array instead of a dictionary for storing the results.
var events: [Event]?
guard let objects = objects else { return }
events = objects.map { object in
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
return Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
}
I finally found it out how can i do it.
I use a struct with what I want like this:
var userDictionary = [Int : Event]()
struct Event {
var nameEvent: String
var nameMagazi: String
}
And then i use this:
if let objects = objects {
for object in objects {
let post = object["idEvent"] as? PFObject
let post2 = post!["idMagazi"] as? PFObject
let nameEvent = post!["name"] as! String
let idEvent = post?.objectId
let nameMagazi = post2!["name"] as! String
self.events[self.i] = Event(nameEvent: nameEvent , nameMagazi: nameMagazi)
self.i += 1
}
print(self.events[1]!.nameEvent)
}
Thank you all for your answers!
Create dictionary like this:
var dict = ["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"]
OR
var dict = Dictionary<String,String>
dict.setValue("Mike", forKey: "name")
A dictionary can be made using this line:
var dictionaryArray: [Dictionary<String,String,Int>] = []
Then values can be added by appending them to the dictionary.
dictionaryArray.append(["name" : "Mike", "lastname" : "vorisis" , "id" : "advsas"])
Hope this helps.

Value of type '[Businessdata]' has no member 'objectAtIndex'

I am try to implement search bar to my table view. But I am getting this error in one function. Don't know how to solve??
Value of type '[Businessdata]' has no member 'objectAtIndex'
My code
var arrDict = [Businessdata]()
func searchMethod(notification:NSNotification)
{
isSearching = true;
let text:String = notification.userInfo!["text"] as! String;
arrSearch = [];
for(var i=0;i<arrDict.count;i++)
{
if((arrDict.objectAtIndex(i).objectForKey("name")?.lowercaseString?.containsString(text.lowercaseString)) == true)
{
arrSearch.addObject(arrDict.objectAtIndex(i));
}
}
TableViewList.reloadData();
}
Edited :
import UIKit
class Businessdata: NSObject {
var BusinessName: String?
var BusinessEmail: String?
var BusinessLatLng: NSArray?
var Address: String?
var ContactNumber: String?
var WebsiteUrl: String?
var Specialities:Array<String>?
var StoreImages: NSArray?
var Languages:Array<String>?
var PaymentMethod:Array<String>?
var OpenHours: [NSDictionary]?
var Rating: Float?
var Updated_date: String?
var FeaturedBusiness: NSDictionary?
init(json: NSDictionary)
{
self.BusinessName = json["business_name"] as? String
self.BusinessEmail = json["business_email"] as? String
self.BusinessLatLng = json["latlng"] as? NSArray
self.Address = json["location"] as? String
self.ContactNumber = json["phone_no"] as? String
self.WebsiteUrl = json["website_url"] as? String
self.Specialities = json["specialities"] as? Array<String>
self.StoreImages = json["images"] as? NSArray
self.Languages = json["languages"] as? Array<String>
self.PaymentMethod = json["method_payment"] as? Array<String>
self.OpenHours = json["opening_hours"] as? [NSDictionary]
self.Rating = json["__v"] as? Float
self.Updated_date = json["updated_at"] as? String
if((json["featured_business"]) != nil)
{
self.FeaturedBusiness = json["featured_business"] as? NSDictionary
}
}
}
Here i have posted the Bussinessdata class code.Now how to solve for my problem
Help me out!!
There is no objectAtIndex in an array. You need to do something like this:
arrDict[i]
Instead of
arrDict.objectAtIndex(i)
Edit
As we discussed in the comments this is what you need
if((arrDict[i].name.lowercaseString?.containsString(text.lower‌​caseString)) == true)
Try this one:
func searchMethod(notification:NSNotification)
{
isSearching = true;
let text:String = notification.userInfo!["text"] as! String;
arrSearch = [];
for(var i=0;i<arrDict.count;i++)
{
if((arrDict[i].BusinessName.lowercaseString?.containsString(text.lower‌​caseString)) == true)
{
arrSearch.addObject(arrDict[i]);// or arrSearch.append(arrDict[i])
}
}
TableViewList.reloadData();
}
objectAtIndex: belongs to NSArray and objectForKey: belongs to NSDictionary.
Both are not available for the Swift native types.
But there are two fatal issues:
Businessdata is a custom class which does not respond to objectForKey: at all, and there is no property name in the class.
Assuming you are talking about the property BusinessName and the logic is supposed to filter all Businessdata instances whose lowercase string of BusinessName contains the search string you might write
arrSearch = [Businessdata]()
for item in arrDict {
if let businessName = item.BusinessName as? String where businessName.lowercaseString.containsString(text.lowercaseString) {
arrSearch.append(item)
}
}
or swifiter
arrSearch = arrDict.filter({ (item) -> Bool in
if let businessName = item.BusinessName as? String {
return businessName.lowercaseString.containsString(text.lowercaseString)
}
return false
})
And please conform to the naming convention and use always variable names starting with a lowercase letter.

Resources