Hello Everyone please let me know what is wrong in this code.
// ContactViewModel.swift import Foundation import Contacts import UIKit final class ContactViewModel : ObservableObject {
@Published
var contacts : [Contact] = []
@Published
var permissionsError : PermissionsError? = .none
init() {
permissions()
}
func openSetting() {
permissionsError = .none
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(settingsURL) { UIApplication.shared.open(settingsURL)}
}
private func getContacts() {
Contact.fetchAll{ [weak self] result in
guard let self = self else { return }
switch result {
case .success(let fetchedContacts):
DispatchQueue.main.async {
self.contacts = fetchedContacts.sorted(by: {$0.lastName < $1.lastName })
}
case .failure(let error):
self.permissionsError = .fetchError(error)
}
}
}
private func permissions() {
switch CNContactStore.authorizationStatus(for: .contacts) {
case .authorized:
getContacts()
case .notDetermined, .restricted, .denied:
CNContactStore().requestAccess(for: .contacts) { [weak self] granted, error in
guard let self = self else { return }
switch granted {
case true:
self.getContacts()
case false:
DispatchQueue.main.async {
self.permissionsError = .userError
}
}
}
default:
fatalError("Unknown Error!")
}
}
}
// ContactModel.swift import Contacts import UIKit struct Contact:Identifiable { var id: String { contact.identifier} var firstName: String {contact.givenName} var lastName: String {contact.familyName} var phone: String? {contact.phoneNumbers.map(.value).first?.stringValue} var contactImage: UIImage? let contact: CNContact
static func fetchAll(_ completion: @escaping(Result<[Contact], Error>) -> Void) {
let containerID = CNContactStore().defaultContainerIdentifier()
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerID)
let keysToFetch = [
CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactPostalAddressesKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey
] as [CNKeyDescriptor]
let request = CNContactFetchRequest(keysToFetch: keysToFetch as [CNKeyDescriptor])
let descriptor = [
CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey,
CNContactPostalAddressesKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey
]
as [CNKeyDescriptor]
do{
let rawContacts = try CNContactStore().unifiedContacts(matching: predicate, keysToFetch: descriptor)
completion(.success(rawContacts.map{.init(contact:$0)}))
} catch {
completion(.failure(error))
}
} }
enum PermissionsError: Identifiable { var id:String { UUID().uuidString } case userError case fetchError(_:Error) var description: String { switch self { case .userError: return "Please change permissions in settings." case .fetchError(let error): return error.localizedDescription } } }
// ContactsView.swift import SwiftUI struct ContactsView: View { @StateObject var contactsVM = ContactViewModel()
let alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
var body: some View {
NavigationStack {
VStack {
ScrollViewReader { scrollProxy in
ZStack {
contactsList(scrollProxy: scrollProxy)
VStack {
ForEach(alphabet, id: \.self) { letter in
HStack {
Spacer()
Button(action: {
print("letter = \(letter)")
if contactsVM.contacts.first(where: { $0.firstName.prefix(1) == letter }) != nil {
withAnimation {
scrollProxy.scrollTo(letter)
}
}
}, label: {
Text(letter)
})
}
}
}
}
}
}
.alert(item: $contactsVM.permissionsError) { _ in
Alert(
title: Text("Permission denied"),
message: Text(contactsVM.permissionsError?.description ?? "unknown error"),
dismissButton:
.default(Text("OK"), action: {contactsVM.openSetting()}))
}
.navigationTitle("Contacts")
}
}
@ViewBuilder
private func contactsList(scrollProxy: ScrollViewProxy) -> some View {
List {
ForEach(contactsVM.contacts.sorted(by: { $0.firstName < $1.firstName })) { contact in
contactSection(contact: contact)
}
}
}
// MARK: - Contact Section
@ViewBuilder
private func contactSection(contact: Contact) -> some View {
Section(header: Text(String(contact.firstName.first ?? "?"))) {
NavigationLink(destination: ContactDetailView(contact:contact)) {
VStack(alignment: .leading){
HStack {
Text(contact.firstName)
}
}
}
}
}
}
struct ContactsView_Previews: PreviewProvider { static var previews: some View { ContactsView() } }
Here is my ContactDetailView.swift import SwiftUI struct ContactDetailView: View { let contact : Contact var body: some View { Text(contact.firstName) } }
struct ContactDetailView_Previews: PreviewProvider { static var previews: some View { ContactDetailView(contact:contact) } }