Creating Contacts with SwiftUI
Learn how to add contacts to the user’s device within a SwiftUI app.
The Contacts framework provides tools for reading, creating, and editing contacts from the device's address book.
Let’s see how to create a contact using CNMutableContact and save the data with a CNSaveRequest in a SwiftUI-based application.
Before moving on, there are two things to address first:
- To be able to work with the user’s contact book your app needs to ask for permission first. You do that by adding the
Privacy - Contacts Usage Descriptionkey to the Info tab of your project settings. - For your application to access contacts, it needs to be tested using the Simulator or running on a device. It won’t work on Xcode Preview.
The article Getting started with the Contacts framework gives you a good introduction on how to use the Contacts framework within a SwiftUI application.
To create a new contact, use the CNMutableContact type. It is a class responsible for representing a new contact that can then be saved in the system. Here is an example:
let contact = CNMutableContact()
contact.givenName = "John"
contact.familyName = "Doe"
contact.phoneNumbers = [
CNLabeledValue(
label: CNLabelPhoneNumberMobile,
value: CNPhoneNumber(stringValue: "+39 333 222 4444")
)
]
Check the documentation page of CNMutableContact to see all the different properties that can be set when creating a new contact programmatically.
To save the contact to the device's contact list, use CNSaveRequest, which represents a save operation to be executed by the contact store.
let saveRequest = CNSaveRequest()
saveRequest.add(contact, toContainerWithIdentifier: nil)
try contactStore.execute(saveRequest)
statusMessage = "Contact saved successfully."
name = ""
phone = ""
Then, when you enter the device's contacts, you will find the contact added. Also include a status message to let the user know the action was successful and reset the name and phone number fields.
Putting it all together in a SwiftUI view you can come up with something like the example below:
import SwiftUI
import Contacts
struct NewContactView: View {
@State private var name: String = ""
@State private var surname: String = ""
@State private var phoneNumber: String = ""
@State private var statusMessage: String = ""
var body: some View {
NavigationStack {
Form {
Section("Indentification") {
TextField("Name", text: $name)
TextField("Surname", text: $surname)
}
Section("Phone Info") {
TextField("Phone", text: $phoneNumber)
.keyboardType(.phonePad)
}
Section {
Button("Save Contact") {
Task {
await saveContact()
}
}
if !statusMessage.isEmpty {
Text(statusMessage)
.foregroundColor(.gray)
.listRowSeparator(.hidden)
}
}
}
.navigationTitle("New contact")
}
}
private let contactStore = CNContactStore()
private func requestAccess() async throws -> Bool {
return try await contactStore.requestAccess(for: .contacts)
}
private func createContact() async -> CNMutableContact? {
let newContact = CNMutableContact()
newContact.givenName = name
newContact.phoneNumbers = [
CNLabeledValue(
label: CNLabelPhoneNumberMobile,
value: CNPhoneNumber(stringValue: phoneNumber)
)
]
return newContact
}
private func saveContact() async {
do {
let accessGranted = try await requestAccess()
guard accessGranted else {
statusMessage = "No permission to access contacts."
return
}
if let newContact = await createContact() {
let saveRequest = CNSaveRequest()
saveRequest.add(newContact, toContainerWithIdentifier: nil)
try contactStore.execute(saveRequest)
statusMessage = "Contact saved successfully."
name = ""
phoneNumber = ""
} else {
statusMessage = "Could not create the new contact."
}
} catch {
statusMessage = "Error saving contact: \(error.localizedDescription)"
}
}
}
You can also isolate the logic for creating and saving contacts in a separate service within your app.