Hello i have variables but gives all of them Optional(). How can i resolve them my codes under below.
Json append codes for koltuklar koltuklaridler array under below you can see
for name in json as! [AnyObject] {
let SeatName = name["SeatName"]
let SeatDesignId = name["SeatDesignId"]
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
let TicketTypeId = blog["TicketTypeId"]
let TicketTypeName = blog["TicketTypeName"]
let Amount = blog["Amount"]
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
Under below you can see tableview inside codes ( That codes doing open koltuklar index path item after search id inside koltuklaridler and when found take some varibles from it )
var koltuklar = [""]
var koltuklaridler = [""]
if let myStrings:String! = koltuklar[indexPath.row]{
print("\(myStrings!)")
let myStringArrf = myStrings.componentsSeparatedByString("*")
print("\(myStringArrf)")
if let koltukisims:String! = String(myStringArrf[0]) {
cell.koltukName.text = koltukisims
}
print(" STR - \(myStringArrf[1] as String!)")
if let index = koltuklaridler.indexOf(myStringArrf[1] as String!) {
let myStringdetaysecilen = koltuklaridler[index]
print("myStringdetaysecilen \(myStringdetaysecilen)")
}
Also my json file
[
{
"SeatDesignId": 16484,
"SeatName": "A6",
"SaloonId": 148,
"SeatDetail": [
{
"TicketTypeId": 1,
"TicketTypeName": "Okay",
"Amount": 13
}
]
},
Output
Optional("A17")*Optional(16254)*
["Optional(\"A17\")", "Optional(16254)", ""]
STR - Optional(16254)
All variables output Optional i try everything but doesn't fix.
As mentioned in my comments, whenever you use String Interpolation "\(...)" make sure that all optional strings are unwrapped. Values read from dictionaries are always optional.
This code unwraps all optional strings
for name in json as! [[String:AnyObject]] {
guard let SeatName = name["SeatName"] as? String,
SeatDesignId = name["SeatDesignId"] as? Int else {
continue
}
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
if let TicketTypeId = blog["TicketTypeId"] as? Int,
TicketTypeName = blog["TicketTypeName"] as? String,
Amount = blog["Amount"] as? Int {
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
}
Edit: I updated the casting to the actual types according to the JSON
Now declare both arrays as empty string arrays.
var koltuklar = [String]()
var koltuklaridler = [String]()
and remove the optional binding in the first line
let myStrings = koltuklar[indexPath.row]
print("\(myStrings)")
...
By the way: Your way to "serialize" the strings with asterisks and deserialize it in the table view is very, very clumsy and inefficient. Use a custom class or struct for the data records.
Your problem is that you are creating a string from values from dict without a if let statement so it returns an optional value:
for name in json as! [AnyObject] {
if let SeatName = name["SeatName"],
let SeatDesignId = name["SeatDesignId"] {
self.koltuklar.append("\(SeatName)*\(SeatDesignId)*")
}
if let blogs = json["SeatDetail"] as? [[String: AnyObject]] {
for blog in blogs {
if let TicketTypeId = blog["TicketTypeId"],
let TicketTypeName = blog["TicketTypeName"],
let Amount = blog["Amount"] {
self.koltuklaridler.append("\(SeatDesignId)*\(TicketTypeId)*\(TicketTypeName)*\(Amount)*")
}
}
}
There is a two way of operate optional.
unwrapped using "!" but in this chances of crash if value is nil.
unwrapped using term call "optional binding" using "if let" condition.
if let var = "assigned your optional variable"{
print(var)
}
You will get your variable without optional.
Related
In this I am getting data from server response after posting parameters and here I need to display it on table view and it should be displayed like shown below in the image 0 is the price for the particular shipping method
already i had written model class for server response data and here it is
struct ShippingMethod {
let carrierCode : String
let priceInclTax : Int
let priceExclTax : Int
let available : Any
let carrierTitle : String
let baseAmount : Int
let methodTitle : String
let amount : Int
let methodCode : String
let errorMessage : Any
init(dict : [String:Any]) {
self.carrierCode = dict["carrier_code"] as! String
self.priceInclTax = dict["price_incl_tax"]! as! Int
self.priceExclTax = dict["price_excl_tax"]! as! Int
self.available = dict["available"]!
self.carrierTitle = dict["carrier_title"] as! String
self.baseAmount = dict["base_amount"]! as! Int
self.methodTitle = dict["method_title"]! as! String
self.amount = dict["amount"]! as! Int
self.methodCode = dict["method_code"] as! String
self.errorMessage = (dict["error_message"] != nil)
}
}
by using this I had formed an array type like this by using code
var finalDict = [String: [String]]()
var responseData = [ShippingMethod]()
do
{
let array = try JSONSerialization.jsonObject(with: data, options: []) as? [[String : Any]]
for item in array! {
self.responseData.append(ShippingMethod.init(dict: item))
}
print(self.responseData)
}
catch let error
{
print("json error:", error)
}
print(self.responseData)
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [String]()
}
self.finalDict[carrierTitle]!.append(methodTitle)
}
print(self.finalDict)
the output of this finalDict is ["Flat Rate": ["Fixed"], "Best Way": ["Table Rate"]] in this carrier title key value pair should be displayed as section title and is Flat Rate and method title key value pair should be displayed as rows in section Fixed but the problem is I need amount key value pair with it also for corresponding method title can anyone help me how to get this ?
Why don't you create another struct for displaying row data:
struct CarrierInfo {
let name:String
let amount:Int
}
Change your finalDict to
var finalDict = [String: [CarrierInfo]]()
and create CarrierInfo instance and set it in finalDict
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [CarrierInfo]()
}
self.finalDict[carrierTitle]!.append(CarrierInfo(name: carrierTitle, amount: amount))
}
Likewise you can make other required changes. This would neatly wrap your row display data inside a structure.
PS: I have not tested the code in IDE so it may contain typos.
You can assign another dictionary with key as methodTitle and amount as value. i.e., ["fixed":"whatever_amount"]
OR
You can use finalDict differently, like ["Flat Rate": ["tilte":"Fixed","amount":"0"], "Best Way": ["title":"Table Rate","amount":"0"]]
If it is difficult for you to code this, you can revert back.
Edit
You can use the following code to create the array in the second solution I suggested above:
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [[String:String]]()
}
let innerDict = ["title":methodTitle,"amount":amount]
self.finalDict[carrierTitle]!.append(innerDict)
}
I have the following class which populates all the "Breakfast" entries from a JSON file. Note that in my JSON file, "ingredients" is an array and "instructions" could be an array of arrays.
In the "Populate" function below, Swift is reporting 2 errors saying, "Type 'String' has no member "PopulateArray". "Type 'AnyObject' has no member "PopulateArray".
How do I fix this?
Here's the Swift Source.
import Foundation
class Breakfast
{
var id:String = ""
var name:String = ""
var image:String = ""
var servings:String = ""
var ingredients:[String] = []
var instructions:[AnyObject] = []
func Populate(dictionary:NSDictionary) {
id = dictionary["id"] as! String
name = dictionary["name"] as! String
image = dictionary["image"] as! String
servings = dictionary["servings"] as! String
ingredients = String.PopulateArray(dictionary["ingredients"] as! [NSArray])
instructions = AnyObject.PopulateArray(dictionary["instructions"] as! [NSArray])
}
class func PopulateArray(array:NSArray) -> [Breakfast]
{
var result:[Breakfast] = []
for item in array
{
let newItem = Breakfast()
newItem.Populate(item as! NSDictionary)
result.append(newItem)
}
return result
}
}
The JSON Source can be found here: JSON Source
Neither String nor AnyObject seem to declare a method PopulateArray (unless it's in class extension you haven't included)
If your JSON has contains an array of strings for the "ingredients" key, then it will be an NSArray of NSString, which can be trivially converted to [String]:
ingredients = dictionary["ingredients"] as! [String]
Likewise, if your JSON always contains an array of AnyObject ([AnyObject]) you can populate it with:
instructions = dictionary["instructions"] as! [AnyObject]
Typically when working with JSON, you'll want to use NSJSONSerialization to convert top level JSON data into their corresponding Swift types, here's a quick example of getting data, running it through a serializer and then accessing the JSON data by casting it into known Swift data types:
if let someJsonData = someOptionalData {
if let jsonTopLevelDictionary = try? NSJSONSerialization.JSONObjectWithData(someJsonData, readingOptions: NSJSONReadingOptions.MutableContainers) as? [String:AnyObject] {
if let stringsArr = jsonTopLevelDictionary!["keyWithArrayOfStrings"] as? [String] {
for string in stringsArr {
// do stuff
}
}
}
}
There are a few different ways to skin this cat, you can even check out something like SwiftyJSON which helps parse JSON data into their native swift types.
I have the following code. the response.result.value is of type Optional(AnyObject), I want to check
it's of type [[String: AnyObject]]
unwrap the optional
check the count of the array
I prefer one line guard over if...return... statement
Alamofire.request(.GET, API.listArticle).responseJSON { response in
print(response.result.value)
guard let articles = response.result.value as? [[String: AnyObject]] where articles.count > 0 else {
return
}
for article in articles {
let entity = NSEntityDescription.insertNewObjectForEntityForName("Article", inManagedObjectContext: DBHelper.context()) as! Article
entity.title = article["title"]
entity.content = article["content"]
}
}
The error is article["content"] line:
Cannot subscript a value of type Dictionary<String, AnyObject> with an index of type String
Also do I need to check if title exist in article? Is it gonna crash or just do nothing?
The problem is that you are using a dictionary where the value has type AnyObject to populate the title and the content properties that are probably String (right?)
You cannot put something that (at compile time) is declared AnyObject into a String property.
Just replace this
entity.title = article["title"]
entity.content = article["content"]
with this
entity.title = article["title"] as? String
entity.content = article["content"] as? String
Update
This updated for will discard articles where the title and content values are not correct Strings.
for article in articles {
if let
title = article["title"] as? String,
content = article["content"] as? String {
let entity = NSEntityDescription.insertNewObjectForEntityForName("Article", inManagedObjectContext: DBHelper.context()) as! Article
entity.title = title
entity.content = content
}
}
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.
I have the following instance method in my class, where "jsonObj" is a Dictionary:
func getSurvey(countryId: String, languageId: String, surveyId: String, userId: String, completionHandler: ((ICSurvey!, NSError!) -> Void)?)
{
ICWebServicesManager.downloadSurvey("", languageId: "", surveyId: "", userId: "") {
(jsonObj, error) -> Void in
if completionHandler != nil
{
if error != nil
{
completionHandler!(nil, error)
}
else
{
let surveyJSONObject = jsonObj
let survey = ICSurvey()
if let surveyIdObj = surveyJSONObject["id"] as? Dictionary<String, String>
{
self.dateFormatter!.dateFormat = "y-M-d'T'HH:mm:ssZZZZ"
survey.id = ICSurveyId(
surveyId : surveyIdObj["survey_id"]!,
countryId : surveyIdObj["country_id"]!,
languageId : surveyIdObj["language_id"]!
)
survey.uri = surveyJSONObject["uri"] as? String
survey.title = surveyJSONObject["title"] as? String
survey.startDate = self.dateFormatter!.dateFromString(surveyJSONObject["start_date"] as! String)
survey.endDate = self.dateFormatter!.dateFromString(surveyJSONObject["end_date"] as! String)
survey.type = surveyJSONObject["type"] as? String
survey.questionGroups = Array()
if let questionGroupsJSONObjects = surveyJSONObject["question_groups"] as? Array<Dictionary<String, AnyObject>>
{
for questionGroupObj in questionGroupsJSONObjects
{
let questionGroup = ICQuestionGroup()
questionGroup.questions = Array()
questionGroup.text = questionGroupObj["text"] as? String
if let questionsArrayObj = questionGroupObj["questions"] as? Array<Dictionary<String, AnyObject>>
{
for questionObj in questionsArrayObj
{
var question = ICQuestion()
if let questionIdObj = questionObj["id"] as? Dictionary<String, String>
{
question.id = ICQuestionId(
questionId : questionIdObj["question_id"]!,
languageId : questionIdObj["language_id"]!
)
question.type = questionObj["type"] as? String
var requiredString = questionObj["required"]
as? String
question.required = (requiredString == "True" ? true : false)
question.setValue(questionObj["text"] as? String, forKey: "text")
if let questionResponseObj = questionObj["response"] as? Dictionary<String, AnyObject>
{
question.response = ICResponse(
type : questionResponseObj["type"] as! String,
value : questionResponseObj["value"] as! Int,
clientTimestamp : self.dateFormatter!.dateFromString(questionResponseObj["client_timestamp"] as! String)!
)
}
if let questionResponseObj = questionObj["options"] as? Array<Dictionary<String, AnyObject>>
{
question.options = questionResponseObj
}
}
questionGroup.questions!.append(question)
}
}
survey.questionGroups!.append(questionGroup)
}
}
}
self.currentSurvey = survey
completionHandler!(self.currentSurvey, error)
}
}
}
}
After doing some code review with my mentor, he told me everything is pretty good, except I need to "abstract out hard wired properties". My understanding is that he does not want to have code that looks like this:
survey.startDate = self.dateFormatter!.dateFromString(surveyJSONObject["start_date"] as! String)
, because "start_date" for instance is hard-coded.
Instead, I should find what objects the JSON file represent and map that data correspondingly. While I agree to that to some extent, as the app code will not need many changes if it do it that way, it seem to be an overhead for me because I need to map everything to Core Data, and if a property changes, many things may change or crash.
My question is: What is the best way to parse JSON files and create Model objects and then map them to the Core Data database?
How do we have to "abstract out hard wired properties"?
If anyone has more experience with Web Services integration, please give me some advice.
Thanks in advance!
I was using SwiftyJSON on previous projects but now I've converted everything to Swift 1.2 (if let,) syntax and I honestly like it more since it feels like I have more flexibility and it alleviates 3rd party framework dependency.
As far as your "hard-coded" values, simply utilize optionals for testing. For instance, the following line requires a value to be present. Test for that value prior to unwrapping it. His fear is that if that object doesn't exist for whatever reason your program will crash. (This isn't tested but you should get the idea)
surveyId : surveyIdObj["survey_id"]!,
if let surveyId = surveyIdObj["survey_id"] as? String,
let countryId = surveyIdObj["country_id"] as? String,
let languageId = surveyIdObj["language_id"] as? String {
survey.id = ICSurveyId(
// You could also test here for each and add the values to your survey object
surveyId : surveyIdObj["survey_id"]!,
countryId : surveyIdObj["country_id"]!,
languageId : surveyIdObj["language_id"]!
)
}
Personally I prefer just creating a new NSManagedObject and assigning the values directly.
var survey = NSEntityDescription.insertNewObjectForEntityForName("Survey", inManagedObjectContext: self.appDelegate.managedObjectContext!) as! Survey
// Then just assign the values to the object using dot notation
survey.surveyId = surveyIdObj["survey_id"]!
// and so on
// Then save the context
self.managedObjectContext?.save(nil)