Section Index Titles after Parsing JSON Swift - ios

I am trying to create dynamic sectionIndexTitles for my tableView. I want to get all of the character names and then get the first letter of the name to create sectionIndexTitles, but I am having difficulty doing so.
I get the JSON from URL:
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "mytestURL")
let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)
session.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
characters = try JSONDecoder().decode([CharacterStats].self, from: data!)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error")
}
}
}.resume()
}
The JSON looks like this:
[
{
"name": "Batman",
"type": "DC",
"gender": "male",
},
{
"name": "Captain America",
"type": "Marvel",
"gender": "male",
},
{
"name": "Captain Marvel",
"type": "Marvel",
"gender": "female",
}
]
And my struct looks like this:
public struct CharacterStats: Decodable {
let name: String
let type: String?
let gender: String?
}

I have posted a sample code for this here https://github.com/achuaswani/ListItemExample.git

Related

How to parse grouped nested dictionary in swift

Please help me out for parsing the below json response in SWIFT5 as I am getting nil values for the user and group data.
{
"user": {
"0": {
"id": "5",
"name": "ABC"
}
},
"group": {
"0": {
"id": "510",
"name": "XYZ"
}
}
}
if let unwrappedData = data {
do{
let json = try JSONSerialization.jsonObject(with: unwrappedData, options: [])
print(json)
if let user = try? JSONDecoder().decode(UserModel.self,from:unwrappedData){
completion(.success(user))
}else{
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: unwrappedData)
completion(.failure(errorResponse.errorValue))
}
}catch{
completion(.failure(error))
}
}
The user data is printing as nil. please help me out to resolve the issue.
I tried below code in playground and it works like charm, what is the issue in json?
Data Model
// MARK: - Sample
struct Sample: Codable {
let user, group: Group
}
// MARK: - Group
struct Group: Codable {
let the0: The0
enum CodingKeys: String, CodingKey {
case the0 = "0"
}
}
// MARK: - The0
struct The0: Codable {
let id, name: String
}
Json Data
let jsonData = """
{
"user": {
"0": {
"id": "5",
"name": "ABC"
}
},
"group": {
"0": {
"id": "510",
"name": "XYZ"
}
}
}
""".data(using: .utf8)
Json Parsing
if let data = jsonData {
let object = try? JSONDecoder().decode(Sample.self, from: data)
print("Json Object", object)
}
else {
print("Bad Json")
}
Output
Json Object Optional(SwiftPlayground.Sample(user: SwiftPlayground.Group(the0: SwiftPlayground.The0(id: "5", name: "ABC")), group: SwiftPlayground.Group(the0: SwiftPlayground.The0(id: "510", name: "XYZ"))))

Error trying to parse JSON using URLSession in swift

I'm trying to parse a test JSON from a http adress, but I get an error saying that
"No value associated with key CodingKeys(stringValue: \"name\", intValue: nil)
The JSON looks like this. It has been validated, so it should work ok:
{
"work": [
{
"name": "Jocke",
"job": "Developer",
"date": "1985-12-30T00:00:00+0000",
"best_book": {
"id": 33245,
"title": "DiscWorld",
"author": {
"id": 345,
"name": "Terry Prattchet"
}
}
},
{
"name": "Bob",
"job": "Construction worker",
"date": "2010-01-30T00:00:00+0000",
"best_book": {
"id": 375802,
"title": "Ender's Game (Ender's Saga, #1)",
"author": {
"id": 589,
"name": "Orson Scott Card"
}
}
}
]
}
The code looks like this:
struct People: Codable {
let name: String
let job: String
enum OuterKey: String, CodingKey {
case work = "work"
}
enum codingKeys: String, CodingKey {
case name = "name"
case job = "job"
}
init(decoder: Decoder) throws {
let outerContainer = try decoder.container(keyedBy: OuterKey.self)
let innerContainer = try outerContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .work)
self.name = try innerContainer.decode(String.self, forKey: .name)
self.job = try innerContainer.decode(String.self, forKey: .job)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://api.myjson.com/bins/fe2eo") else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedJson = try! jsonDecoder.decode([People].self, from: data)
}
}.resume()
}
}
I'm just trying to grasp the first two keys as of now, just to see if it works. But it doesn't even get past name.
Your api returns
[{"firstName":"Jocke","job":"developer"},{"firstName":"Anna","job":"construction"},{"firstName":"Peter","job":"pilot"}]
do {
let jsonDecoder = JSONDecoder()
let decodedJson = try jsonDecoder.decode([People].self, from: data)
}
catch {
print(error)
}
struct People: Codable {
let firstName, job: String
}
just try this
struct Work:Codeable {
let work:[People]
}
struct People: Codable {
let name: String
let job: String
}
do {
let jsonDecoder = JSONDecoder()
let decodedJson = try jsonDecoder.decode(Work.self, from: data)
}
catch {
print(error)
}
if you have same name as json keys you don't need to use codingkeys

assigning "Get" Request data to Text field

My get data response is like
I want "title" and "date" should be shown in my view controller "label values"
get method calls when app running and the data should display in either text fields "or" in label
My Code is
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { fatalError() }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
out put is :
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
{
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "Shanna#melissa.tv",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": {
"lat": "-43.9509",
"lng": "-34.4618"
}
},
]
I want to print "username":
"email":
values in my Storyboard labels
The result contains multiple users, so you should first iterate over them and find the user you want. Then you can set text on your UI elements in the Main thread.
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { fatalError() }
typealias User = [String: Any]
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let usersJson = try JSONSerialization.jsonObject(with: data, options: []) as! [User]
print(usersJson)
// Since the result is an array of users
for user in usersJson {
guard let userName = user["username"] as? String else { return assertionFailure("Invalid username") }
print(userName)
// All UI works should done in main thread
DispatchQueue.main.async {
<#usernameLabel#>.text = username
}
}
} catch {
print(error)
}
}
}.resume()
I suggest you take a look at Swift Codable. It will boost your coding and minimize syntax and human errors.

How to parse json data include page number by codable in Swift 4 and Alamofire

I'm a new with Swift and I'm confusing to get data instead of using SwiftyJson by using Codable.
The format Json data type like as:
{
"current_page": 1,
"total_page": 407,
"new_entries": [
{
"id": 10174,
"title": "Hello",
"description": "Hello",
"categories": "women",
"image": "imagelink",
"url": "urllink",
"date": "time",
"is_favorite": false
},
{
"id": 9237,
"title": "hi",
"description": "hi",
"categories": "skincare",
"image": "imagelink",
"url": "url",
"date": "time",
"is_favorite": false
},
So how do I get the entries and decode and save to codable like
let decoder = JSONDecoder()
do {
let feed = try decoder.decode(Feed.self, from: jsonData)
print(feed.title)
} catch {
print(error.localizedDescription)
}
I stop at this below and dont know how to convert the json["new_entries"] to string type and decode.
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
debugPrint(response)
if let JSON = response.result.value as? NSDictionary {
GlobalVariables.sharedManager.pageCurr = JSON["current_page"] as? Int
GlobalVariables.sharedManager.pageTotal = JSON["total_page"] as? Int
if let entries = JSON["new_entries"] as? NSArray{
for entry in entries {
if let entry = entry as? NSDictionary {
for (key, value) in entry {
print("\(key) - \(value)")
}
}
}
}
}
My Feed Struct
struct Feed: Codable {
enum CodingKeys: String, CodingKey {
case id
case title
case description
case categories
case image
case url
case date
case favorite = "is_favorite"
}
let id: Int
let title: String
let description: String
let categories: String
let image: String
let url: String
let date: String
let favorite: Bool
}
Thanks so much.
You need
struct Root: Codable {
let currentPage, totalPage: Int
let newEntries: [Feed]
}
struct Feed: Codable {
let id: Int
let title, description, categories, image: String
let url, date: String
}
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseData { response in
debugPrint(response)
guard let data = response.data else { return }
do {
let de = JSONDecoder()
de.keyDecodingStrategy = .convertFromSnakeCase
let res = try de.decode(Root.self, from: data)
print(res.currentPage)
print(res.totalPage)
print(res.newEntries)
}
catch {
print(error)
}
}
correct json
{
"current_page": 1,
"total_page": 407,
"new_entries": [
{
"id": 10174,
"title": "Hello",
"description": "Hello",
"categories": "women",
"image": "imagelink",
"url": "urllink",
"date": "time",
"is_favorite": false
},
{
"id": 9237,
"title": "hi",
"description": "hi",
"categories": "skincare",
"image": "imagelink",
"url": "url",
"date": "time",
"is_favorite": false
}]
}
I added a new main struct
struct Main: Codable {
let currentPage: Int
let totalPage: Int
let feeds: [Feed]
enum CodingKeys: String, CodingKey {
case currentPage = "current_page"
case totalPage = "total_page"
case feeds = "new_entries"
}
}
and then decode using that struct
let decoder = JSONDecoder()
do {
let result = try decoder.decode(Main.self, from: jsonData)
print(result.currentPage)
} catch {
print(error)
}
or with the alamofire example
Alamofire.request("https://abc.jp/api/category/women_all/?page=1", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseData { response in
guard let data = response.data else { return }
do {
let decoder = JSONDecoder()
let result = try decoder.decode(Main.self, from: data)
GlobalVariables.sharedManager.pageCurr = result.currentPage
GlobalVariables.sharedManager.pageTotal = result.totalPage
for feed in result.feeds {
print(feed.id)
//etc
}
} catch {
print(error)
// other error handling
}

Value of type '[Root]' has no member 'commit'. How can i decode JSON root array

I am trying to parse JSON using codable. I am able to decode it but it is in a root array and i am unable to print each value on by it self. the compiler is complaining saying Value of type '[Root]' has no member 'commit'. How can i change this to print the values. Below is the JSON
[
{
"sha": "3f4227ec2894bb354b145deff9dbc1adc6b6d6f2",
"node_id": "MDY6Q29tbWl0NDQ4Mzg5NDk6M2Y0MjI3ZWMyODk0YmIzNTRiMTQ1ZGVmZjlkYmMxYWRjNmI2ZDZmMg==",
"commit": {
"author": {
"name": "Slava Pestov",
"email": "sviatoslav.pestov#gmail.com",
"date": "2018-08-12T08:09:22Z"
}
}
},
{
"sha": "3f4227ec2894bb354b145deff9dbc1adc6b6d6f2",
"node_id": "MDY6Q29tbWl0NDQ4Mzg5NDk6M2Y0MjI3ZWMyODk0YmIzNTRiMTQ1ZGVmZjlkYmMxYWRjNmI2ZDZmMg==",
"commit": {
"author": {
"name": "Slava Pestov",
"email": "sviatoslav.pestov#gmail.com",
"date": "2018-08-12T08:09:22Z"
}
}
}
]
I decode it here
struct Root: Decodable {
let commit: Author
}
struct Author: Decodable {
let author: People
}
struct People: Decodable {
let name: String?
let date: String?
let email: String?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getCommits()
}
func getCommits() {
let urlString = "https://api.github.com/repos/apple/swift/commits"
guard let url = URL(string: urlString) else {
print("Couldn't fetch JSON")
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: url) { (data, response, error) in
guard data != nil && error == nil else {
print(data ?? "")
return
}
do {
let decoder = JSONDecoder()
let result = try decoder.decode([Root].self, from: data!)
print(result.commit)
print(result.commit.author.name)
} catch let decodeError {
print("Failed to decode json:", decodeError)
}
}
dataTask.resume()
}
}
And here is my output in the console. I would like to be able to print only the name, date and email.
[Gihhub.Commits(commit: Gihhub.Author(author: Gihhub.People(name: Optional("Slava Pestov"), date: Optional("2018-08-12T08:09:22Z"), email: Optional("sviatoslav.pestov#gmail.com")))), Gihhub.Commits(commit: Gihhub.Author(author: Gihhub.People(name: Optional("Slava Pestov"), date: Optional("2018-08-12T03:47:22Z"), email: Optional("spestov#apple.com")))), Gihhub.Commits(commit: Gihhub.Author(author: Gihhub.People(name: Optional("Slava Pestov"), date: Optional("2018-08-12T03:47:08Z"), email: Optional("spestov#apple.com"))))]
result is an array you need
result.forEach {
print($0.commit.author.name)
print($0.commit.author.date)
print($0.commit.author.email)
}

Resources