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)
}
}
1
0
977