Getting JSON from database multidimensional array

Hey All,

I am new to swift and i am having some issues with my JSON request. I am able to retrieve the data and i can output this into my ViewController but a part of the data i get back from my database is inside a multidimensional array. I want to take this data and loop through it. (sometimes this part contains 1 items, sometimes 2 sometimes 3 etc so its pretty dynamic)

I do everything programmaticly, so i use no storyboards.

This is a sample JSON response:
How can take the todo and create an array which i can loop through?

When i say

            for item in json["todo"]  {
                
                print(item)
            }

i get:
Value of optional type 'NSDictionary?' must be unwrapped to refer to member 'subscript' of wrapped base type 'NSDictionary'

My JSON

{
"firstname": "Wouter ",
"lastname": "SwiftySwift",
"profilepicture": "https://res.cloudinary.com/www-gipaa-nl/image/upload/w_400,h_400,c_crop,g_face,r_max,d_avatar.png,bo_25px_solid_white/w_200/v1551042302/images/1551035640e793a635f61e",
"aboutme": "This is test data, This is test data. ",
"acceptall": "1",
"todo": [
{
"id": "31",
"title": "Door",
"done": "0"
},
{
"id": "32",
"title": "Window",
"done": "0"
},
{
"id": "33",
"title": "test",
"done": "0"
},
{
"id": "34",
"title": "test",
"done": "0"
},
{
"id": "35",
"title": "test",
"done": "0"
},
{
"id": "36",
"title": "test",
"done": "0"
}
]
}

My Code

//
// MemberController.swift
// GIPAA
//
// Created by Wouter on 16/02/2019.
// Copyright © 2019 . All rights reserved.
//

import Foundation
import UIKit

extension UIImageView {
func dowloadFromServer(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() {
self.image = image
}
}.resume()
}
func dowloadFromServer(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
dowloadFromServer(url: url, contentMode: mode)
}
}
class MemberController: UIViewController {

lazy var scrollView: UIScrollView = {
    let view = UIScrollView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.contentSize.height = 2000
    view.backgroundColor = UIColor.white
    
    return view
}()


let mainMenu: UIButton = {
    let image = UIImage(named: "menu.PNG")
    let l = UIButton(type: .system)
    l.addTarget(self ,action: #selector(openMenu), for: .touchUpInside)
    l.setBackgroundImage(image, for: .normal)
    
  
    return l
    
}()


let profileView:  UIImageView = {
let p = UIImageView()
p.image = UIImage(named: "default.png")

p.layer.borderWidth = 14.0
p.clipsToBounds = true
p.layer.borderColor = UIColor.white.cgColor
    
return p
    
}()


let memberTable: UITableView = {
    
    let m = UITableView()
  
    m.backgroundColor = orangeBanner
    m.separatorStyle = .none
    return m
    
}()

let memberInfoTable: UITableView = {
    
    let m = UITableView()
    
    m.backgroundColor = UIColor.white
    m.separatorStyle = .none
    return m
    
}()

let cell1: UITableViewCell = {
    let m = UITableViewCell()
    m.backgroundColor = UIColor.white
    
    return m
}()

let cell2: UITableViewCell = {
    let m = UITableViewCell()
    m.backgroundColor = grayBanner
    
    return m
}()


let mijnProfielField: UILabel = {
    
    let t = UILabel()
    t.text = "Mijn profiel"
    t.font = UIFont.boldSystemFont(ofSize: 18.0)
    
    t.textColor = UIColor.white
    return t
    
}()

let interesseButton: UIButton = {
    
    let h = UIButton()
    h.setTitle("   Interesses   ", for: .normal)
    h.setTitleColor(UIColor.gray, for: .normal)
    h.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
    h.titleLabel?.font = h.titleLabel?.font.withSize(16)
    h.addTarget(self ,action: #selector(openInteresses), for: .touchUpInside)
    return h
    
}()
let compButton: UIButton = {
    
    let h = UIButton()
    h.setTitle("Competenties", for: .normal)
    h.setTitleColor(UIColor.gray, for: .normal)
    h.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
    h.titleLabel?.font = h.titleLabel?.font.withSize(16)

    
    
    return h
    
}()
let beoorButton: UIButton = {
    
    let h = UIButton()
    h.setTitle("Beoordelingen", for: .normal)
    h.setTitleColor(UIColor.gray, for: .normal)
    h.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
    h.titleLabel?.font = h.titleLabel?.font.withSize(16)

    return h
    
}()

let underMember=UIView()


let nameField: UILabel = {
    
    let t = UILabel()
    
    t.font = UIFont.boldSystemFont(ofSize: 14.0)
    
    t.textColor = UIColor.black
    return t
    
}()


let aboutMeLabel: UILabel = {
    
    let t = UILabel()
    
    t.font = UIFont.boldSystemFont(ofSize: 10.0)
    t.numberOfLines = 18
    t.textColor = UIColor.black
    t.backgroundColor = grayBanner
    return t
    
}()

let aboutmeTitle: UILabel = {
    
    let t = UILabel()
    t.text = "Over mij:"
    t.font = UIFont.boldSystemFont(ofSize: 12.0)
    
    t.textColor = UIColor.black
    return t
    
}()

let underTable=UIView()

let ikStaOpenLabel: UILabel = {
    
    let t = UILabel()
    t.text = "Ik sta open voor alles"
    t.font = UIFont.boldSystemFont(ofSize: 14.0)
    t.numberOfLines = 18
    t.textColor = UIColor.black
    return t
    
}()


let ikStaOpenButton: UIButton = {
    
    let h = UIButton()
   
    h.addTarget(self ,action: #selector(changeOpenVoor), for: .touchUpInside)
    h.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
    h.titleLabel?.font = h.titleLabel?.font.withSize(16)
    
    return h
    
}()

let intTitleTable: UITableView = {
    
    let m = UITableView()
    
    m.backgroundColor = orangeColor
    m.separatorStyle = .none
    return m
    
}()


let todoInfoTable: UITableView = {
    
    let m = UITableView()
    
    m.backgroundColor = UIColor.white
    m.separatorStyle = .none
   
    return m
    
}()

let todocell1: UITableViewCell = {
    let m = UITableViewCell()
    m.backgroundColor = orangeBanner
    
    
    return m
}()

let todocell2: UITableViewCell = {
    let m = UITableViewCell()
    m.backgroundColor = grayBanner
    
    return m
}()
 let todoTitle: UILabel = {
    
    let t = UILabel()
    t.text = "To do list"
    t.font = UIFont.boldSystemFont(ofSize: 18.0)
    
    t.textColor = UIColor.white
    return t
    
}()


let addToDo: UIButton = {
    
    let h = UIButton()
    h.setTitle("+", for: .normal)
    h.addTarget(self ,action: #selector(addtodo), for: .touchUpInside)
    h.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
    h.titleLabel?.font = h.titleLabel?.font.withSize(26)
    h.setTitleColor(UIColor.white, for: .normal)

    return h
    
}()


override func viewDidLoad() {
    super.viewDidLoad()

    
    view.backgroundColor = .white


    
    let myUrl = URL(string: "https://gipaa.nl/app/member_json.php");
    
    var request = URLRequest(url:myUrl!)
    
    request.httpMethod = "POST"// Compose a query string
    
    let accessToken: String? = KeychainWrapper.standard.string(forKey: "token")
    let userId: String? = KeychainWrapper.standard.string(forKey: "id")
    
    
    let postString = "userId=\(userId!)&token=\(accessToken!)";
    
    request.httpBody = postString.data(using: String.Encoding.utf8);
    
    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
        
        if error != nil
        {
            print("error=\(error)")
            return
        }
        
        // You can print out response object
       // print("response = \(response)")
        
        //Let's convert response sent from a server side script to a NSDictionary object:
        do {
            let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
            let firstname = json?["firstname"]
            let lastname = json?["lastname"]
            let profilepicture = json!["profilepicture"]
            let aboutme = json?["aboutme"]
            **var todo = json?["todo"]**
            
         
            

            
            DispatchQueue.main.async
                {
            
            self.nameField.text = "\(firstname!)  \(lastname!)"
                    
                    
                    if profilepicture is NSNull {

                        
                    }else{
                        
                        self.profileView.dowloadFromServer(link: profilepicture! as! String, contentMode: .scaleAspectFill)
                        
                        self.view.addSubview(self.profileView)
                    
                        
                    }
                    if aboutme is NSNull {
                        
                        self.aboutMeLabel.text = "Geen informatie"
                    }else{
                        
                        self.aboutMeLabel.text = "\(aboutme!)"
                    
                    }
                  
                   
             
            }
            
            

        } catch {
            print(error)
        }
    }
    task.resume()
    
    
setupScrollView()
}

override func viewWillLayoutSubviews() {
    profileView.layer.cornerRadius = profileView.frame.size.width / 2
}


private func setupScrollView(){
    view.addSubview(scrollView)
    
    
    scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    scrollView.keyboardDismissMode = .onDrag
    scrollView.isScrollEnabled = true
    
    
    
    
    
    
    // Do any additional setup after loading the view
    
    let imageView = UIImageView(frame: CGRect(x: 10, y: 5, width: 150, height: 50))
    imageView.image = UIImage(named: "main.PNG")
    
    scrollView.addSubview(imageView)
    
    
    
    scrollView.addSubview(mainMenu)
    
    mainMenu.translatesAutoresizingMaskIntoConstraints = false
    mainMenu.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 5).isActive = true
    
    
    mainMenu.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    mainMenu.heightAnchor.constraint(equalToConstant: 50).isActive = true
    mainMenu.widthAnchor.constraint(equalToConstant: 50).isActive = true
    
    
    
    
    view.addSubview(memberTable)
    
    memberTable.translatesAutoresizingMaskIntoConstraints = false
    
    memberTable.topAnchor.constraint(equalTo: mainMenu.bottomAnchor, constant: 10).isActive = true
    memberTable.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
    memberTable.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
    memberTable.heightAnchor.constraint(equalToConstant: 50).isActive = true
    
    
    

    
    
    view.addSubview(mijnProfielField)
    
    mijnProfielField.translatesAutoresizingMaskIntoConstraints = false
    
    mijnProfielField.topAnchor.constraint(equalTo: mainMenu.bottomAnchor, constant: 5).isActive = true
    
    mijnProfielField.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
    mijnProfielField.widthAnchor.constraint(equalToConstant: 120).isActive = true
    mijnProfielField.heightAnchor.constraint(equalToConstant: 60).isActive = true
    
    
    view.addSubview(interesseButton)
    
    interesseButton.translatesAutoresizingMaskIntoConstraints = false
    
    interesseButton.topAnchor.constraint(equalTo: mijnProfielField.bottomAnchor, constant: 10).isActive = true
    interesseButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
    
    interesseButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
    
    
    
    
    view.addSubview(compButton)
    
    compButton.translatesAutoresizingMaskIntoConstraints = false
    
    compButton.topAnchor.constraint(equalTo: mijnProfielField.bottomAnchor, constant: 10).isActive = true
    compButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -10).isActive = true
    
    compButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
    
    
    
    
    
    view.addSubview(beoorButton)
    
    beoorButton.translatesAutoresizingMaskIntoConstraints = false
    
    beoorButton.topAnchor.constraint(equalTo: mijnProfielField.bottomAnchor, constant: 10).isActive = true
    beoorButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
    
    beoorButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
    
    
    
    
    
    
    view.addSubview(underMember)
    
    underMember.translatesAutoresizingMaskIntoConstraints = false
    
    underMember.topAnchor.constraint(equalTo: beoorButton.bottomAnchor, constant: 15).isActive = true
    underMember.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    underMember.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    underMember.heightAnchor.constraint(equalToConstant: 2).isActive = true
    
    underMember.backgroundColor=grayBanner
    
    // Add rounded corners to UIView
    underMember.layer.cornerRadius=0
    
    // Add border to UIView
    
    
    
    
    
    
    view.addSubview(memberInfoTable)
    
    memberInfoTable.translatesAutoresizingMaskIntoConstraints = false
    
    memberInfoTable.topAnchor.constraint(equalTo: underMember.bottomAnchor, constant: 10).isActive = true
    memberInfoTable.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    memberInfoTable.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    memberInfoTable.heightAnchor.constraint(equalToConstant: 340).isActive = true
    
    
    
    view.addSubview(cell1)
    
    cell1.translatesAutoresizingMaskIntoConstraints = false
    
    cell1.topAnchor.constraint(equalTo: underMember.bottomAnchor, constant: 10).isActive = true
    cell1.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    cell1.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    cell1.heightAnchor.constraint(equalToConstant: 80).isActive = true
    
    
    
    view.addSubview(cell2)
    
    cell2.translatesAutoresizingMaskIntoConstraints = false
    
    cell2.topAnchor.constraint(equalTo: cell1.bottomAnchor, constant: 0).isActive = true
    cell2.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    cell2.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    cell2.heightAnchor.constraint(equalToConstant: 240).isActive = true
    

    
    view.addSubview(profileView)
    
    profileView.translatesAutoresizingMaskIntoConstraints = false
    profileView.topAnchor.constraint(equalTo: underMember.bottomAnchor, constant: 0).isActive = true
    profileView.centerXAnchor.constraint(equalTo: self.cell2.centerXAnchor).isActive = true
    profileView.heightAnchor.constraint(equalToConstant: 140).isActive = true
    profileView.widthAnchor.constraint(equalToConstant: 140).isActive = true
    
    
    
    view.addSubview(nameField)
    
    nameField.translatesAutoresizingMaskIntoConstraints = false
    nameField.topAnchor.constraint(equalTo: profileView.bottomAnchor, constant: -10).isActive = true
    
    nameField.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    nameField.heightAnchor.constraint(equalToConstant: 50).isActive = true
    
    view.addSubview(aboutmeTitle)
    
    aboutmeTitle.translatesAutoresizingMaskIntoConstraints = false
    aboutmeTitle.topAnchor.constraint(equalTo: nameField.bottomAnchor, constant: 10).isActive = true
    
    aboutmeTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    
    
    
    view.addSubview(aboutMeLabel)
    
    aboutMeLabel.translatesAutoresizingMaskIntoConstraints = false
    aboutMeLabel.topAnchor.constraint(equalTo: aboutmeTitle.bottomAnchor, constant: 1).isActive = true
    aboutMeLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -32).isActive = true
    aboutMeLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    

    
    view.addSubview(underTable)
    
    underTable.translatesAutoresizingMaskIntoConstraints = false
    
    underTable.topAnchor.constraint(equalTo: memberInfoTable.bottomAnchor, constant: 10).isActive = true
    underTable.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    underTable.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    underTable.heightAnchor.constraint(equalToConstant: 2).isActive = true
    
    underTable.backgroundColor=grayBanner
    
    // Add rounded corners to UIView
    underTable.layer.cornerRadius=0
    
    view.addSubview(todoInfoTable)
    
    todoInfoTable.translatesAutoresizingMaskIntoConstraints = false
    todoInfoTable.topAnchor.constraint(equalTo: underTable.bottomAnchor, constant: 20).isActive = true
    todoInfoTable.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    todoInfoTable.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    
    
    view.addSubview(todocell1)
    
    todocell1.translatesAutoresizingMaskIntoConstraints = false
    todocell1.topAnchor.constraint(equalTo: underTable.bottomAnchor, constant: 20).isActive = true
    todocell1.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    todocell1.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    todocell1.heightAnchor.constraint(equalToConstant: 60).isActive = true
    todocell1.layer.cornerRadius = 12
    todocell1.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
    
    
    view.addSubview(todocell2)
    
    todocell2.translatesAutoresizingMaskIntoConstraints = false
    todocell2.topAnchor.constraint(equalTo: todocell1.bottomAnchor, constant: 0).isActive = true
    todocell2.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
    todocell2.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 16).isActive = true
    todocell2.heightAnchor.constraint(equalToConstant: 200).isActive = true
    
    view.addSubview(todoTitle)
    
    todoTitle.translatesAutoresizingMaskIntoConstraints = false
    todoTitle.centerYAnchor.constraint(equalTo: self.todocell1.centerYAnchor).isActive = true

    todoTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
    
    view.addSubview(addToDo)
    
    addToDo.translatesAutoresizingMaskIntoConstraints = false
    addToDo.centerYAnchor.constraint(equalTo: self.todocell1.centerYAnchor).isActive = true
    
    addToDo.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -24).isActive = true

}

@objc func addtodo(){
    
    let todo = NewToDoController()
    present(todo, animated: true, completion: nil)
}
@objc func openInteresses(){
    
    let interesses = InteressesController()
    present(interesses, animated: true, completion: nil)
}

@objc func openMenu(){
    
    let menu = MenuController()
    present(menu, animated: true, completion: nil)
}

@objc func changeOpenVoor(){
    
    let accessToken: String? = KeychainWrapper.standard.string(forKey: "token")
    let userId: String? = KeychainWrapper.standard.string(forKey: "id")
 

    let request = NSMutableURLRequest(url: NSURL(string: "https://gipaa.nl/app/changeopen_json.php")! as URL)
    request.httpMethod = "POST"
    let postString = "userId=\(userId!)&token=\(accessToken!)&change=yes"
    
    request.httpBody = postString.data(using: String.Encoding.utf8)
    
    let task = URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in
        
        // ensure there is no error for this HTTP response
        guard error == nil else {
            print ("error: \(error!)")
            return
        }
        
        // ensure there is data returned from this HTTP response
        guard let content = data else {
            print("No data")
            return
        }
        
        // serialise the data / NSData object into Dictionary [String : Any]
        guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
            print("Not containing JSON")
            
            
            
            return
            
        }
      
        
        
        let melding = json["melding"] as? String
        
      
        if(melding == "success"){
            
            
            DispatchQueue.main.async
                {
                    
                    self.viewDidLoad()
                    self.viewWillAppear(true)
                    
            }
            
            
            
            
            
        }
        
        if(melding == "Error"){
            
            self.displayMessage(userMessage: "Er is een onbekende fout opgetreden.")
            print("error=\(String(describing: error))")
            return
        }
        
        
    }
    
    task.resume()
    
}




func displayMessage(userMessage:String) -> Void {
    DispatchQueue.main.async
        {
            let alertController = UIAlertController(title: "Alert", message: userMessage, preferredStyle: .alert)
            
            let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
                // Code in this block will trigger when OK button tapped.
                print("Ok button tapped")
            }
            alertController.addAction(OKAction)
            self.present(alertController, animated: true, completion:nil)
    }
}

}

You might have more luck if you edit your post to improve the formatting and focus on the specific problem you’re having. With regards that second point, is the problem you’re having with:

  • Fetching the JSON?

  • Converting JSON to some Swift type?

  • Using that Swift type once you’ve fetched it?

  • Working with UIKit?

If you focus on one of these areas, it makes it easier for folks to help out. And if you’re having problems with multiple areas, ask about one of them first, and then move on to the next.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

I think my issues lies mainly in Converthing the JSON into a swift type (array) and then looping through it.
For instance what would i do if i would like to print all titles from my json file.

Start by defining a struct that maps to your JSON and conforms to the Codable protocol:

struct Response: Codable {
    var firstname: String
    var lastname: String
    … more stuff here …
    var todo: [ToDoItem]

    struct ToDoItem: Codable {
        var id: String
        var title: String
        var done: String
    }
}

Now, assuming the JSON from your first post is in a Data value called jsonData, decode as follows:

let response = try JSONDecoder().decode(Response.self, from: jsonData)
let titles = response.todo.map { $0.title }
print(titles) // -> ["Door", "Window", "test", "test", "test", "test"]

Beyond that, it’d probably be a good idea to tidy up your structs to be more Swift-like. For example:

  • You could use custom coding keys to change firstname to firstName.

  • You could manually implement Codable to make done a Bool.

If you’d like to learn more, check out Encoding and Decoding Custom Types.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

2 Likes