skip to Main Content

I’ve been trying to solve this for a couple of days now. Whenever I add a new contact and load it from Realm, a new section is created with the same header. Every section has only one row and cannot accommodate more. I’m trying to accomplish functionality like the contacts app on the phone. I want every section to hold as many names (rows) as possible with unique names.

Here’s my code:

class ArtistNamesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var contacts: [Contact] = []
var contactsArray = [[Contact]]()

override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(didTapAdd))
        
        loadArtists()
    }

func setupContacts() {
        let groupedContacts = Dictionary(grouping: contacts) { (contact) -> Character in
            return contact.name.first!
        }
        let keys = groupedContacts.keys.sorted()
        keys.forEach { (key) in
            contactsArray.append(groupedContacts[key]!)
        }

    }
}

//MARK: - Realm Methods

func uploadContacts(contactName: String, contactNumber: String) {
    
    let contact = ContactRealm()
    contact.artistName = contactName
    contact.artistPhoneNumber = contactNumber
    
    let realm = try! Realm()
    try! realm.write {
        realm.add(contact)
    }
}

func loadArtists(){

    let contactsRealm = realm.objects(ContactRealm.self)
    
    for contact in contactsRealm {
        if let name = contact.artistName,
           let number = contact.artistPhoneNumber {
            self.contactsArray.append([Contact(name: name, number: number)])
        }
    }
    
    self.tableView.reloadData()
}

//MARK: - TableView Methods

func numberOfSections(in tableView: UITableView) -> Int {
    return contactsArray.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return contactsArray[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ArtistNamesCell", for: indexPath) as! ContactsTableViewCell
    cell.configureName(with: contactsArray[indexPath.section][indexPath.row].name)
    cell.configureNumber(with: contactsArray[indexPath.section][indexPath.row].number)
    return cell
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let title = String(contactsArray[section].first?.name.prefix(1) ?? "")
    return title
}

//MARK: - Segue

@IBAction func goToContacts(segue: UIStoryboardSegue) {
    
    guard let viewController = segue.source as? AddArtistViewController else { return }
    guard let name = viewController.name.text,
          let phoneNumber = viewController.phoneNumber.text else {
              return
          }
            
    contactsArray = []
    let contact = Contact(name: name, number: phoneNumber)
    contacts.append(contact)
    uploadContacts(contactName: name, contactNumber: phoneNumber)
    setupContacts()
    tableView.reloadData()
}
}

ContactRealm Class

class ContactRealm: Object {
    @objc dynamic var artistName: String?
    @objc dynamic var artistPhoneNumber: String?
}

Contact Class

import Foundation

struct Contact {
    let name: String
    let number: String
}

enter image description here

Thank you in advance.

2

Answers


  1. Chosen as BEST ANSWER

    It was solved using this extension:

    public enum SortingOrder {
        case ascending
        case descending
    }
    
    public extension Sequence {
        func group<K: Hashable & Comparable>(
            by keyForValue: (Element) -> K,
            sortOrder: SortingOrder = .ascending) -> [[Element]] {
            Dictionary(grouping: self, by: keyForValue)
                .sorted { sortOrder == .ascending ? $0.key > $1.key : $0.key < $1.key }
                .map { $0.value }
        }
    }
    

    And adding...

    contactArray = contacts.group(by: { $0.name.first ?? "-" }, sortOrder: .descending) to loadArtists()

    Also, setupContacts() has been changed to:

    contactArray = contacts.group(by: { $0.name.first ?? "-" }, sortOrder: .descending)
    

    • Because you are returning multiple sections in

      func numberOfSections(in tableView: UITableView) -> Int {
         return contactsArray.count // HERE ITS RETURNING THE A, B , C , D MULTIPLE TIMES.
      }
      

    Solution 1: // Bit Complex

    • Return the unique Alphabet Set/ Array from

    setArray.append(String(contactsArray[section].first?.name.prefix(1) ?? ""))

    • return this setArray.count in numberOfSections.

    • in numberOfRowsInSection filter out array with items and return which are present with Alphabet. e.g let rowsCount = contactArray.filter({$0.name. prefix(1) == setArray[section] })

    • return row which belongs this group in cellForRowAt.let contact = contactArray.filter({$0.name. prefix(1) == setArray[indexpath.section]})[indexpath.row] as? Contact

    Solution 2:

    • Create Unique dictionary that maintains Alphabet and contact objects.
      e.g let contactDict = [ 'A': [Contact(name: name, number: number), Contact(name: name, number: number)]], 'B': [Contact(name: name, number: number), Contact(name: name, number: number)]] // This list you can update dynamically once you received contact from database.

    • return this Array(contactDict.keys).count in numberOfSections.

    • in numberOfRowsInSection return let rowsCount = contactsDict[Array(contactsDict.keys).sorted()[section]].count

    • return row which belongs this group in cellForRowAt.let contact = contactsDict[Array(contactsDict.keys).sorted()[section]][indexpath.row]

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search