type any? has no subscript members - arrays

I want to get Addresses from profile dictionary,but I got the error "type any? has no subscript members"
var address:[[String : Any]] = [["Address": "someLocation", "City": "ABC","Zip" : 123],["Address": "someLocation", "City": "DEF","Zip" : 456]]
var profile:[String : Any] = ["Name": "Mir", "Age": 10, "Addresses": address]
profile["Addresses"][0] <-----------------type any? has no subscript members
How can I fix it and get the address? Thanks a lot.

When you subscript profile with "Addresses", you're getting an Any instance back. Your choice to use Any to fit various types within the same array has caused type erasure to occur. You'll need to cast the result back to its real type, [[String: Any]] so that it knows that the Any instance represents an Array. Then you'll be able to subscript it:
func f() {
let address: [[String : Any]] = [["Address": "someLocation", "City": "ABC","Zip" : 123],["Address": "someLocation", "City": "DEF","Zip" : 456]]
let profile: [String : Any] = ["Name": "Mir", "Age": 10, "Addresses": address]
guard let addresses = profile["Addresses"] as? [[String: Any]] else {
// Either profile["Addresses"] is nil, or it's not a [[String: Any]]
// Handle error here
return
}
print(addresses[0])
}
This is very clunky though, and it's not a very appropriate case to be using Dictionaries in the first place.
In such a situation, where you have dictionaries with a fixed set of keys, structs are a more more appropriate choice. They're strongly typed, so you don't have to do casting up and down from Any, they have better performance, and they're much easier to work with. Try this:
struct Address {
let address: String
let city: String
let zip: Int
}
struct Profile {
let name: String
let age: Int
let addresses: [Address]
}
let addresses = [
Address(
address: "someLocation"
city: "ABC"
zip: 123
),
Address(
address: "someLocation"
city: "DEF"
zip: 456
),
]
let profile = Profile(name: "Mir", age: 10, addresses: addresses)
print(profile.addresses[0]) //much cleaner/easier!

You should re-think how you've chosen to construct adress and profile; see e.g. Alexander Momchliov's answer.
For the technical discussion, you could extract the Any members of profile that you know to contain [String: Any] dictionaries wrapped in an Any array; by sequential attempted type conversion of profile["Addresses"] to [Any] followed by element by element (attempted) conversion to [String: Any]:
if let adressDictsWrapped = profile["Addresses"] as? [Any] {
let adressDicts = adressDictsWrapped.flatMap{ $0 as? [String: Any] }
print(adressDicts[0]) // ["Zip": 123, "City": "ABC", "Address": "someLocation"]
print(adressDicts[1]) // ["Zip": 456, "City": "DEF", "Address": "someLocation"]
}
or, without an intermediate step ...
if let adressDicts = profile["Addresses"] as? [[String: Any]] {
print(adressDicts[0]) // ["Zip": 123, "City": "ABC", "Address": "someLocation"]
print(adressDicts[1]) // ["Zip": 456, "City": "DEF", "Address": "someLocation"]
}
But this is just a small lesson in attempted typed conversion (-> don't do this).

I agree that if you rethink your design as suggested earlier. For discussion sake you can perform the following to achieve what you are seeking.
var address:[[String : Any]] = [["Address": "someLocation", "City": "ABC","Zip" : 123],["Address": "someLocation", "City": "DEF","Zip" : 456]]
var profile:[String : Any] = ["Name": "Mir", "Age": 10, "Addresses": address]
if let allAddresses = profile["Addresses"] as? [[String:Any]] {
print("This are all the address \(allAddresses[0])")
}

Related

Access array within an array of JSON response in swift?

I'm trying to access an array response within array this is how I'm getting the friends array successfully
let url = URL(string: "http://xyz/api/get-friends-in-meetings")
AF.request(url!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON{ response in
switch response.result {
case .success:
let responseValue = response.value as! NSDictionary
let friends: [NSDictionary] = responseValue["friends"] as! [NSDictionary]
//here I want to store the "meeting array so that I can use it later"
break
case .failure(let error):
print(error)
break
}
}
This is the JSON response
{
"status": 0,
"message": "Friends found.",
"friends": [
{
"id": 24,
"name": "XYZ",
"email": "Z#Y.Z",
"meetings": [
{
"meeting_with": "X",
"meeting_person_screen_name": "Y",
"meeting_purpose": "Z",
}
]
}
]
}
how do I get the "meetings" array which is within the "friends" array? I have tried the same way as I did for friends but it show an error No exact matches in call to subscript
First you need to define these structs:
struct DataResult : Decodable {
let status: Int
let message: String
let friends: [FriendsResult]
}
struct FriendsResult : Decodable {
let id: Int
let name: String
let email: String
let meetings: [MeetingsResult]
}
struct MeetingsResult: Decodable {
let meeting_with: String
let meeting_person_screen_name: String
let meeting_purpose: String
}
So after that we need the JSON Example:
// Meetings
let meeting1: [String: Any] = [
"meeting_with": "X",
"meeting_person_screen_name": "Y",
"meeting_purpose": "Z"
]
// Friends
let friend1: [String: Any] = [
"id" : 24,
"name": "XYZ",
"email": "x#y.z",
"meetings": [meeting1]
]
let friend2: [String: Any] = [
"id" : 25,
"name": "John Doe",
"email": "jd#x.doe",
"meetings": [meeting1]
]
// Main JSON
let jsonExample: [String : Any] = [
"status": 0,
"message": "Friends found.",
"friends": [friend1, friend2]
]
Well, we continue to validate the JSON and decode to "DataResult":
let valid = JSONSerialization.isValidJSONObject(jsonExample)
if valid {
do {
let dataResult = try JSONSerialization.data(withJSONObject: jsonExample, options: JSONSerialization.WritingOptions())
let dataDecode = try JSONDecoder().decode(DataResult.self, from: dataResult)
let jsonString = String(data: dataResult, encoding: .utf8)
print("JSON: \(String(describing: jsonString))")
if dataDecode.friends.count > 0 {
// Get first friend you should use guard
let friend = dataDecode.friends[0]
let meeting = friend.meetings[0]
print("\(friend.name)")
print("\(meeting.meeting_with)")
}
}
catch let error {
print("ERROR: \(error.localizedDescription)")
}
}
else {
print("Invalid JSON")
}

Decoding JSON array on Swift

I have a simple array that I want to decode. I can deal with arrays in a JSON format with creating a new struct in the model class, and attributing that struct as an array in the main struct. But in this case, the json data is already on an array with 1 element. Therefore I should get the first element in the json response. I think I need to provide a decoder before I can access anything, but I don't know how that decoder model should be. The error I'm getting is:
typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))
JSON data I want to decode: (notice the data is on a array)
[
{
"name": "United States",
"topLevelDomain": [
".us"
],
"alpha2Code": "US",
"alpha3Code": "USA",
"callingCodes": [
"1"
],
"capital": "Washington, D.C.",
"altSpellings": [
"US",
"USA",
"United States of America"
],
"region": "Americas",
"subregion": "Northern America",
"population": 321645000,
"latlng": [
38,
-97
],
"demonym": "American",
"area": 9629091,
"gini": 48,
"timezones": [
"UTC-12:00",
"UTC-11:00",
"UTC-10:00",
"UTC-09:00",
"UTC-08:00",
"UTC-07:00",
"UTC-06:00",
"UTC-05:00",
"UTC-04:00",
"UTC+10:00",
"UTC+12:00"
],
"borders": [
"CAN",
"MEX"
],
"nativeName": "United States",
"numericCode": "840",
"currencies": [
"USD",
"USN",
"USS"
],
"languages": [
"en"
],
"translations": {
"de": "Vereinigte Staaten von Amerika",
"es": "Estados Unidos",
"fr": "États-Unis",
"ja": "アメリカ合衆国",
"it": "Stati Uniti D'America"
},
"relevance": "3.5"
} ]
model class:
struct CountryModel: Codable {
let country: [MyResponse]
}
struct MyResponse: Codable {
let name: String
let capital: String
}
Manager class:
struct CountryManager {
let countryURL = "https://restcountries-v1.p.rapidapi.com/name/"
func fetchData(_ countryName: String) {
let urlString = "\(countryURL)\(countryName)"
print(urlString)
performRequest(urlString)
}
func performRequest(_ urlString: String){
if let url = URL(string: urlString){
var request = URLRequest(url:url)
request.setValue("x-rapidapi-key", forHTTPHeaderField: "myapikey")
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if let e = error {
print(e)
return
}
if let safeData = data {
self.parseJSON(safeData)
}
}
task.resume()
}
}
func parseJSON(_ countryData: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([CountryModel].self, from: countryData)
print(decodedData[0].country)
}
catch {
print(error)
}
}
}
Most of the fields are missing from your model. Here is how it should look like instead:
struct Country: Codable {
let name: String
let topLevelDomains: [String]
let alpha2Code: String
let alpha3Code: String
let callingCodes: [String]
let capital: String
let altSpellings: [String]
let region: String
let subregion: String
let population: Int
let latlng: [Int]
let demonym: String
let area: Int
let gini: Int
let timezones: [String]
let borders: [String]
let nativeName: String
let numericCode: String
let currencies: [String]
let languages: [String]
let translation: Translation
let relevance: String
}
struct Translation: Codable {
let de: String
let es: String
let fr: String
let ja: String
let it: String
}
I found out that there was a problem in my http request. I used Alamofire in the request part and I don't experience the problem anymore. The issue seemed to be related to the decoding but I don't know. I'm posting the final code if anyone experiences the same issue.
import Foundation
import Alamofire
struct CountryManager {
let countryURL = "https://restcountries-v1.p.rapidapi.com/name/"
func fetchData(_ countryName: String) {
let urlString = "\(countryURL)\(countryName)"
print(urlString)
performRequest(urlString)
}
func performRequest(_ urlString: String){
let headers: HTTPHeaders = [
"x-rapidapi-host": "restcountries-v1.p.rapidapi.com",
"x-rapidapi-key": "apices"
]
AF.request(urlString, headers: headers).responseJSON { response in
self.parseJSON(response.data!)
}
}
func parseJSON(_ countryData: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CountryAlias.self, from: countryData)
print(decodedData[0].name)
}
catch {
print(error)
}
}
}

Decode a JSON array

I'm just learning the Swift Decodable protocol and am running into a problem. I am able to decode one json object into a swift object, but am stuck with decoding an array.
What goes well:
imagine following json:
let json = """
{
"all" : {
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
}
"""
let jsonData = json.data(using: .utf8)
I can decode it to a Fact object with following code:
enum Type: String, Decodable {
case cat = "cat"
}
struct Fact {
let id: String
let text: String
let type: Type
let upvotes: Int
enum CodingKeys: CodingKey {
case all
}
enum FactKeys: CodingKey {
case _id, text, type, upvotes
}
}
extension Fact: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let allContainer = try container.nestedContainer(keyedBy: FactKeys.self, forKey: .all)
id = try allContainer.decode(String.self, forKey: ._id)
text = try allContainer.decode(String.self, forKey: .text)
type = try allContainer.decode(Type.self, forKey: .type)
upvotes = try allContainer.decode(Int.self, forKey: .upvotes)
}
}
let decoder = JSONDecoder()
let fact = try decoder.decode(Fact.self, from: jsonData!)
But the api is giving me an array of objects:
let json = """
{
"all": [
{
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
},
{
"_id": "5b01a447c6914f0014cc9a30",
"text": "The special sensory organ called the Jacobson's organ allows a cat to have 14 times the sense of smell of a human. It consists of two fluid-filled sacs that connect to the cat's nasal cavity and is located on the roof of their mouth behind their teeth.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
]
}
"""
let jsonData = json.data(using: .utf8)
And I want to store that in an allFacts array that hold my Fact objects
class Facts: ObservableObject {
#Published var allFacts = [Fact]()
}
let decoder = JSONDecoder()
let allFacts = try decoder.decode([Fact].self, from: jsonData!)
I'm using the same extension on my Fact struct. But it's giving me an error and I am totally lost for a second. Any idea how I can solve this ?
Do I need to create codingKeys for the class as well ?
Expected to decode Array<Any> but found a dictionary instead."
I recommend not to mess around with nested containers. This is less efficient than the default stuff. In your case you would have to use nestedUnkeyedContainer and iterate the array which is still more expensive.
Instead just add a root struct
let json = """
{
"all": [
{
"_id": "59951d5ef2db18002031693c",
"text": "America’s cats, including housecats that adventure outdoors and feral cats, kill between 1.3 billion and 4.0 billion birds in a year.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
},
{
"_id": "5b01a447c6914f0014cc9a30",
"text": "The special sensory organ called the Jacobson's organ allows a cat to have 14 times the sense of smell of a human. It consists of two fluid-filled sacs that connect to the cat's nasal cavity and is located on the roof of their mouth behind their teeth.",
"type": "cat",
"user": {
"_id": "5a9ac18c7478810ea6c06381",
"name": {
"first": "Alex",
"last": "Wohlbruck"
}
},
"upvotes": 4,
"userUpvoted": null
}
]
}
"""
let jsonData = Data(json.utf8)
enum Type: String, Decodable {
case cat
}
struct Root : Decodable {
let all : [Fact]
}
struct Fact : Decodable {
let id: String
let text: String
let type: Type
let upvotes: Int
enum CodingKeys : String, CodingKey {
case id = "_id", text, type, upvotes
}
}
let decoder = JSONDecoder()
let root = try decoder.decode(Root.self, from: jsonData)
print(root.all)

How can I serialize Array of JSON with arrays from a file in path?

I have a .json file into my Xcode project using Swift.
I need load the content and parse to use in my controller, butn when I try serialize the file content to an jsonObject I'm getting errors parsing...
I have read another similar questions but I haven't found a similar Array of JSON to read with different structures contained and another Array of JSON into the object.
The format of array JSON is:
[
{
"title": "The App",
"description": "This is the description",
"friends": [
{
"name": "Gary",
"image": "http://",
"description": "Nice"
},
{
"name": "Patri",
"image": "http://",
"description": "Amazing"
},
{
"name": "Lucy",
"image": "http://",
"description": "Up"
}
]
}
]
I'm using this code to get the content of file from bundle path(data.json added to my proyect) and then serialize, but always get an error because Friends contains an array of json.
let path = Bundle.main.path(forResource: "data", ofType: "json")
let jsonData = try! Data(contentsOf: URL(fileURLWithPath: path!))
let jsonResult = try! JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String:Any]] //Here is the error parsing the array of Friend of JSON
How can I parse this array of json that contains another array of jsonObjects?
You need to use Codable
// MARK: - Element
struct Root: Codable {
let title, purpleDescription: String
let friends: [Friend]
enum CodingKeys: String, CodingKey {
case title
case purpleDescription = "description"
case friends
}
}
// MARK: - Friend
struct Friend: Codable {
let name, image, friendDescription: String
enum CodingKeys: String, CodingKey {
case name, image
case friendDescription = "description"
}
}
let url = Bundle.main.url(forResource: "data", withExtension: "json")
let jsonData = try! Data(contentsOf:url)
let res = try! JSONDecoder().decode([Root].self,from:data)
print(res)

How to Assign Multidimensional Array Values to Tableview Cell swift 3

var QuestionID = [String]()
var Questions = [String]()
var AnswersID = [[String]]()
var AnswersArray = [[String]]()
var AnswerType = [String]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell{
let cell =
tblQuickQuestions.dequeueReusableCell(withIdentifier:
"idQuickQuestionsTableViewCell", for: indexPath) as!
QuickQuestionsTableViewCell
cell.backgroundColor = UIColor.white
cell.selectionStyle = .none
cell.lblRadio!.text = AnswersArray[indexPath.sections]
[indexPath.rows]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int{
return AnswersArray[section].count
}
/*********************************************************************************************/
My Array Values:
Questions ID: ["2", "3", "4", "5", "6"]
Question: ["How is the flooring?", "Test Question ???", "what is ur bdate", "Test Question", "How are you ...?"]
Answer Type: ["Checkbox", "Checkbox", "Drop Down Menu", "Drop Down Menu", "Radio Buttons"]
Answers ID: [["3"], ["5"], ["6", "7", "8"], ["9"], ["10", "11", "12"]]
AnswersArray: [["test"], ["ans"], ["2 April", "7 jan", "15aug"], ["lklklk"], ["aa", "bb", "cc"]]
I want to implement an Question, Answers Quiz in which Answers are of multiple choice and can be of type Radio Buttons, Checkboxes or Lists. I have implemented Two dimensional array for answers like: Answers[["1","2","3"],["1"],["1","2","3","4","5"]]. where First inner array represents answers for first question, second inner array for second question and so on. and for Questions like: Questions["Question1","Question2","Question3"]. Now I want to assign these values to label present in table view cell and wants to display next Question and answers on next button click.
You can solve this problem by creating Singleton array of Dictionary.
Suppose my singleton array name is 'MainArray'.
So i have created one function which will be called from viewDidLoad() function.
func gatherData(){
MainArray.removeAll(keepingCapacity: false)
let QuestionsID : [String] = ["2", "3", "4", "5", "6"]
let Question : [String] = ["How is the flooring?", "Test Question ???", "what is ur bdate", "Test Question", "How are you ...?"]
let AnswerType : [String] = ["Checkbox", "Checkbox", "Drop Down Menu", "Drop Down Menu", "Radio Buttons"]
// let AnswersID : [[String]] = [["3"], ["5"], ["6", "7", "8"], ["9"], ["10", "11", "12"]]
let AnswersArray : [[String]] = [["test"], ["ans"], ["2 April", "7 jan", "15aug"], ["lklklk"], ["aa", "bb", "cc"]]
var emptyDictionary = [String: Any]()
for ind in 0...QuestionsID.count-1{
emptyDictionary["QID"] = QuestionsID[ind]
emptyDictionary["QNAME"] = Question[ind]
emptyDictionary["ANSWERTYPE"] = AnswerType[ind]
var innerArray : [String] = []
for inner in 0...AnswersArray.count-1{
innerArray = [AnswersArray[ind][inner]]
break
}
emptyDictionary["ANSWERARRAY"] = innerArray
emptyDictionary["ANSWER_USER"] = ""
MainArray.append(emptyDictionary)
}
print("MainArray:\(MainArray)")
}
Output from console:
MainArray:[["ANSWERARRAY": ["test"], "ANSWER_USER": "", "ANSWERTYPE": "Checkbox", "QNAME": "How is the flooring?", "QID": "2"], ["ANSWERARRAY": ["ans"], "ANSWER_USER": "", "ANSWERTYPE": "Checkbox", "QNAME": "Test Question ???", "QID": "3"], ["ANSWERARRAY": ["2 April"], "ANSWER_USER": "", "ANSWERTYPE": "Drop Down Menu", "QNAME": "what is ur bdate", "QID": "4"], ["ANSWERARRAY": ["lklklk"], "ANSWER_USER": "", "ANSWERTYPE": "Drop Down Menu", "QNAME": "Test Question", "QID": "5"], ["ANSWERARRAY": ["aa"], "ANSWER_USER": "", "ANSWERTYPE": "Radio Buttons", "QNAME": "How are you ...?", "QID": "6"]]
By using MainArray index position you can switch between array values which is nothing but single dictionary for that index, and you can display results as per the data in dictionary.
If this solves your problem, don't forget to accept the answer.

Resources