diff --git a/nirvana-ios.xcodeproj/project.pbxproj b/nirvana-ios.xcodeproj/project.pbxproj index 4c31123..9e67f33 100644 --- a/nirvana-ios.xcodeproj/project.pbxproj +++ b/nirvana-ios.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ 89BDCDEF2769B50C00577829 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89BDCDEE2769B50C00577829 /* ContactsView.swift */; }; 89BDCDF12769E64200577829 /* Firestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89BDCDF02769E64200577829 /* Firestore.swift */; }; 89D99D2E2766FD6B00237814 /* Liquid in Frameworks */ = {isa = PBXBuildFile; productRef = 89D99D2D2766FD6B00237814 /* Liquid */; }; + 89F05B9B276A87920002E9C7 /* ContactsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F05B9A276A87920002E9C7 /* ContactsViewModel.swift */; }; + 89F05B9F276A8D360002E9C7 /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F05B9E276A8D360002E9C7 /* Contact.swift */; }; 89F1386D2767EB19006680ED /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89F1386C2767EB19006680ED /* SignInView.swift */; }; 89F138702767F55F006680ED /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 89F1386F2767F55F006680ED /* GoogleSignIn */; }; /* End PBXBuildFile section */ @@ -71,6 +73,8 @@ 89BDCDEB2769918D00577829 /* OnboardingTrioView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTrioView.swift; sourceTree = ""; }; 89BDCDEE2769B50C00577829 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = ""; }; 89BDCDF02769E64200577829 /* Firestore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firestore.swift; sourceTree = ""; }; + 89F05B9A276A87920002E9C7 /* ContactsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsViewModel.swift; sourceTree = ""; }; + 89F05B9E276A8D360002E9C7 /* Contact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = ""; }; 89F1386C2767EB19006680ED /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -200,6 +204,7 @@ isa = PBXGroup; children = ( 891A160D27657CEA00B26659 /* User.swift */, + 89F05B9E276A8D360002E9C7 /* Contact.swift */, ); path = Models; sourceTree = ""; @@ -242,6 +247,7 @@ isa = PBXGroup; children = ( 89BDCDEE2769B50C00577829 /* ContactsView.swift */, + 89F05B9A276A87920002E9C7 /* ContactsViewModel.swift */, ); path = Contacts; sourceTree = ""; @@ -341,6 +347,7 @@ files = ( 89BDCDEC2769918D00577829 /* OnboardingTrioView.swift in Sources */, 891A15E327653A7000B26659 /* ContentView.swift in Sources */, + 89F05B9F276A8D360002E9C7 /* Contact.swift in Sources */, 891A160327656FC700B26659 /* HomeView.swift in Sources */, 891A160B2765732700B26659 /* HomeViewModel.swift in Sources */, 892179E62765FECD001E6C60 /* FooterControlsViewModel.swift in Sources */, @@ -352,6 +359,7 @@ 891A1611276590B000B26659 /* ChatsCarouselView.swift in Sources */, 89BDCDE527695DB900577829 /* NirvanaImage.swift in Sources */, 891A16132765A44300B26659 /* ChatsCarouselViewModel.swift in Sources */, + 89F05B9B276A87920002E9C7 /* ContactsViewModel.swift in Sources */, 891A15E127653A7000B26659 /* nirvana_iosApp.swift in Sources */, 891A160127656FB200B26659 /* FooterView.swift in Sources */, 89BDCDEA27697F9C00577829 /* UserMenu.swift in Sources */, @@ -497,6 +505,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "nirvana-ios/Info.plist"; + INFOPLIST_KEY_NSContactsUsageDescription = "Please provide access to build your inner circle 🌱"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -529,6 +538,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "nirvana-ios/Info.plist"; + INFOPLIST_KEY_NSContactsUsageDescription = "Please provide access to build your inner circle 🌱"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/nirvana-ios/Info.plist b/nirvana-ios/Info.plist index 69e7e18..7082d13 100644 --- a/nirvana-ios/Info.plist +++ b/nirvana-ios/Info.plist @@ -2,6 +2,8 @@ + NSContactsUsageDescription + Please provide access to build your inner circle 🌱 CFBundleURLTypes diff --git a/nirvana-ios/Models/Contact.swift b/nirvana-ios/Models/Contact.swift new file mode 100644 index 0000000..70aaf0e --- /dev/null +++ b/nirvana-ios/Models/Contact.swift @@ -0,0 +1,32 @@ +// +// Contact.swift +// nirvana-ios +// +// Created by Arjun Patel on 12/15/21. +// + +import Foundation +import Contacts + +struct Contact: Identifiable, Hashable { + var id = UUID() + var firstName: String + var lastName: String + var phoneNumbers: [String] + var emailAddresses: [String] +} + +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 + } + } + +} diff --git a/nirvana-ios/Views/Contacts/ContactsView.swift b/nirvana-ios/Views/Contacts/ContactsView.swift index 16302b2..161f668 100644 --- a/nirvana-ios/Views/Contacts/ContactsView.swift +++ b/nirvana-ios/Views/Contacts/ContactsView.swift @@ -6,6 +6,8 @@ // import SwiftUI +import Contacts +import ContactsUI struct ContactsView: View { var testUsers:[TestUser] = [ @@ -16,16 +18,23 @@ struct ContactsView: View { TestUser(_profilePic: "rohan", _firstN: "Rohan", _lastN: "Chadha") ] + @ObservedObject var contactsViewModel = ContactsViewModel() + var body: some View { - List { - ForEach(testUsers) { user in - let fullName = "\(user.firstName) \(user.lastName)" - IndividualContactView(name: fullName, imageName: user.profilePictureUrl) - } + VStack(spacing: 0) { + OnboardingTemplateView(imgName: "undraw_grades_re_j7d6", mainLeadingActText: "Add your ", mainHighlightedActText: "closest folks.", mainTrailingActText: "", subActText: "besties, bf or gf, siblings, parents, etc.") + + Rectangle() + .strokeBorder(style: StrokeStyle(lineWidth: 4, dash: [10])) + + } + .background(NirvanaColor.bgLightGrey) + .alert(item: $contactsViewModel.permissionsError) {_ in + Alert(title: Text("Permissions Needed"), + message: Text(contactsViewModel.permissionsError?.description ?? "unknown error"), + dismissButton: .default(Text("Ok"), action: contactsViewModel.openSettings)) } } - - } struct ContactsView_Previews: PreviewProvider { @@ -40,7 +49,7 @@ private struct IndividualContactView: View { @Namespace private var animation @State var isAddedUser = false - + var body: some View { HStack(alignment: .center, spacing: 0) { Image(imageName) diff --git a/nirvana-ios/Views/Contacts/ContactsViewModel.swift b/nirvana-ios/Views/Contacts/ContactsViewModel.swift new file mode 100644 index 0000000..e60216e --- /dev/null +++ b/nirvana-ios/Views/Contacts/ContactsViewModel.swift @@ -0,0 +1,68 @@ +// +// ContactsViewModel.swift +// nirvana-ios +// +// Created by Arjun Patel on 12/15/21. +// + +import Foundation +import Contacts +import SwiftUI + +final public class ContactsViewModel : ObservableObject { + @Published var contacts : [Contact] = [] + @Published var permissionsError: PermissionsError? = .none + + init() { + self.permissions() + } + + func openSettings() { + self.permissionsError = .none + guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return } + if UIApplication.shared.canOpenURL(settingsURL) { UIApplication.shared.open(settingsURL)} + } + + private func fetchContacts() { + //remove all current contacts from vm + contacts.removeAll() + + let store = CNContactStore() + + do { + let predicate = CNContact.predicateForContacts(matchingName: "sarth") + //specific contact details: ex: name, email, etc. + let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey] as [CNKeyDescriptor] + + let contacts = try store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) + + print("Fetched contacts: \(contacts)") + } catch { + print("Failed to fetch contacts, error: \(error)") + // Handle the error + self.permissionsError = .fetchError(error) + } + } + + private func permissions() { + switch CNContactStore.authorizationStatus(for: .contacts) { + case.authorized: + print("fetching contacts") + fetchContacts() + case .notDetermined, .restricted, .denied: + CNContactStore().requestAccess(for: .contacts) {granted, error in + switch granted { + case true: + self.fetchContacts() + case false: + DispatchQueue.main.async { + print("unable to get contacts") + self.permissionsError = .userError + } + } + } + default: + fatalError("Unknown Error!") + } + } +}