UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

What's the best way to return nested data?

Forums > SwiftUI

Hi Everyone,

Me again.

I have a bogus database to test my project with. This database has several 'accounts', and each account has its own unique 'contacts'. So think of the 'accounts' as grocery stores, and the 'contacts' as employees at each unique store. These unique contacts should appear in a per account basis. So that when you look at an account, only the contacts belonging to that account are shown. When I run my app with what I have so far, it returns all of the contacts from the database. Not just the ones belonging to their respective accounts.

I feel like I'm getting close here but ultimately end up lost again and frustrated. I'll put my code below, and if anyone can shed some light on what I'm doing wrong, would be very appreciated. Shoutout to @Roosterboy for being a huge help this wknd! :)

struct AccountView: View {
    @State private var accounts = AccountList.activeAccounts
    var body: some View {

Section {
            ForEach(accounts) { acct in
                ForEach(acct.contacts) { cont in
                    SubTextFields(
                        leftSideField: "\(cont.positionDepartment)",
                         rightSideField: ("\(cont.firstName) \(cont.lastName)"))
                    }
                }
       }
 }

And the below is what my DB looks like now:

struct Account: Identifiable, Codable {
    var id = UUID()
    let company: String
    let address: String
    let city: String
    let postalCode: String
    let phoneOne: String
    let phoneTwo: String
    let emailAddress: String
    let website: String
    let contacts: [ContactData]
    let quotes: [QuoteData] 
    let notes: [NoteData]
}

struct ContactData: Identifiable, Codable {
    var id = UUID()
    let firstName: String
    let lastName: String
    let companyName: String
    let positionDepartment: String
    let accountManager: String
    let email: String
    let cellNumber: String
    let officeNumber: String
    let billingAddress: String
    let city: String
    let province: String
    let postalCode: String
}

struct AccountList: Codable {
    var accounts: [Account]

    static let activeAccounts: [Account] = [
        Account(
            company: "First Coffee House",
            address: "xxxxxxxx Main Ave",
            city: "xxxxxxx",
            postalCode: "D4F 5D3",
            phoneOne: "xxx-xxx-xxxxx", 
            phoneTwo: "xxx-xxx-xxxxx",
            emailAddress: "xxxxx@xxxxxx.com",
            website: "",
            contacts: [ 
                ContactData(
                    firstName: "Al",
                    lastName: "Carter",
                    companyName: "xx xxxxxxxx xxxxxxx",
                    positionDepartment: "Inside",
                    accountManager: "xxxxxx",
                    email: "xxxxx@xxxxxx.xxxx",
                    cellNumber: "xxx-xxx-xxxxx",
                    officeNumber: "xxx-xxx-xxxxx",
                    billingAddress: "xxxxx Main Rd",
                    city: "xxxxxxx",
                    province: "xx",
                    postalCode: "F3D 6S3"
                ),
                ContactData(
                    firstName: "Bob ",
                    lastName: "Smith",
                    companyName: "xx xxxxxxxx xxxxxxx",
                    positionDepartment: "Counter",
                    accountManager: "xxxxxx",
                    email: "xxxxx@xxxxxx.xxxx",
                    cellNumber: "xxx-xxx-xxxxx",
                    officeNumber: "xxx-xxx-xxxxx",
                    billingAddress: "xxxxx Main Rd",
                    city: "xxxxxxxxx",
                    province: "xx",
                    postalCode: "F3D 6S3"
                )
            ]
        )
    ]
}

Any help is always appreciated, or even a good source to check out! Thanks!

2      

So in this example, contacts will return Al Carter and Bob Smith, since they are nested inside the First Coffee House account.

Similarly, however your contacts are nested in their accounts in your real data, that's how they will be returned.

So, say for instance you were loading the data from a JSON file like this:

{
    "accounts": [
        {
            "company": "First Coffee House",
      "address": "1234 5th Street",
            "contacts": [
                {
                    "firstname": "Bob",
                    "lastname": "Smith"
                },
                {
                    "firstname": "Al",
                    "lastname": "Carter"
                }
            ]
        },
        {
            "company": "Central Banking Office",
      "address": "87 Royal View Road",
            "contacts": [
                {
                    "firstname": "Charlotte",
                    "lastname": "Grote"
                },
                {
                    "firstname": "Shauna",
                    "lastname": "Wickle"
                }
            ]
        }
    ]
}

Then loading just the account for Central Banking Office would load the contacts for Charlotte Grote and Shauna Wickle.

But if your JSON was structured like this:

{
    "accounts": [
        {
            "company": "First Coffee House",
      "address": "1234 5th Street"
        },
        {
            "company": "Central Banking Office",
      "address": "87 Royal View Road"
        }
    ],
    "contacts": [
        {
            "firstname": "Bob",
            "lastname": "Smith",
            "company": "First Coffee House"
        },
        {
            "firstname": "Al",
            "lastname": "Carter",
            "company": "First Coffee House"
        },
        {
            "firstname": "Charlotte",
            "lastname": "Grote",
            "company": "Central Banking Office"
        },
        {
            "firstname": "Shauna",
            "lastname": "Wickle",
            "company": "Central Banking Office"
        }
    ]
}

Then you would need to read in all your contacts and then filter them by company.

So it really all depends on how your data is being stored and read into the application. Based on the Account struct above, the code you have will only return Al Carter and Bob Smith, which seems like it should be correct. If you are seeing something different in your real data, then take another look at how it is structured and pulled into the program.

2      

Hi RoosterBoy - Thanks again for your insights.

So as it is now, my DB looks more lke the first iteration. So I have multiple contacts that are attached to one account. I also have multiple accounts. When I run the view however, all of the contacts are displayed.

Is there a way to make it so just the contacts per account are displayed?

2      

This code:

ForEach(accounts) { acct in
    ForEach(acct.contacts) { cont in
        SubTextFields(
            leftSideField: "\(cont.positionDepartment)",
            rightSideField: ("\(cont.firstName) \(cont.lastName)"))
    }
}

loops through all accounts and displays their contacts. If you just want to display the contacts from a single Account, you need to somehow indicate which Account you are working with and then only loop through its contacts.

Something like this, perhaps:

struct AccountView: View {
    let account: Account

    var body: some View {
        Section {
            ForEach(account.contacts) { contact in
                SubTextFields(
                    leftSideField: contact.positionDepartment,
                    rightSideField: "\(contact.firstName) \(contact.lastName)"
                )
            }
        }
    }
}

And you would set account when you create AccountView by passing in an Account instance from whatever the parent View is.

2      

It's fixed! Thank you again for your help! I've got to apply this to several other features, so I'll be learning this really well. Education through repetition!

Cheers! (There's no way someone can by you a coffee here like on other sites is there?)

2      

It strikes me that what you're really doing is designing a simple relational database. I'd suggest creating two entirely separate struct arrays, one of accounts and one of contacts. Rather than using the account name (which can change) to link the contacts and the accounts, it would be better to add a UUID to the Accounts struct and include the appropriate Accounts UUID in each instance of the Contacts struct. It's then straightforward to find all the contacts of a particular account, and given a particular contact, easy to find the account to which it relates.

It might be a little overkill for this instance, but it's good practice. There's a reason that relational databases have become so common!

Jeremy

2      

There's no way someone can by you a coffee here like on other sites is there?

Nah, just mark the thread as SOLVED if a post has solved your problem. We're all here to learn and to help if we can, after all.

3      

SOLVED!

2      

@kikashi59 I'll definitely look into this! I don't think it's overkill at all. I've actually got quite a few fields and objects so I think it might work out.

Cheers!

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.