capacitor-community / contacts Goto Github PK
View Code? Open in Web Editor NEWContacts Plugin for Capacitor
Home Page: https://capacitor-community.github.io/contacts/
Contacts Plugin for Capacitor
Home Page: https://capacitor-community.github.io/contacts/
Describe the bug
After sync the contact to server. If i add a new contact in phone.not able to find it. Do we have any solution to find the newly add contact or last updated timestamp?
Describe the bug
I am trying to use this plugin with Ionic 5 + Vue 3 since the Cordova Contact plugin seems to be deprecated however, I am unable to get it to work and I am unable to find any documentation related to Ionic 5 + Vue 3 + Capactor plugins that works. If you have any insight, I would appreciate it.
To Reproduce
The code below is what I have tried. Essentially, there's a button that calls "chooseContacts" but nothing happens and the iOS console log simply shows "error" and nothing else.
<script>
import { defineComponent } from 'vue';
import { Plugins } from '@capacitor/core'
export default defineComponent({
mounted() {
this.contacts = Plugins
},
methods: {
chooseContacts() {
this.contacts.getContacts().then(results => {
console.log(results)
})
}
}
});
</script>
Expected behavior
I am expecting the console log to show a list of contacts as the result.
Screenshots
NA
Desktop (please complete the following information):
Smartphone (please complete the following information):
Describe the solution you'd like
I wonder if it would possible to have a method inside the plugin to pick a single contact by using the OS native UI as the Cordova deprecated plugin did through the method pickContact.
Describe alternatives you've considered
Additional context
Documentation and typescript mention that method saveContact has image parameter, and that you can set contact image. However native code (android and iOS) has no such feature.
Parameter 'image', passed in TypeScript angular application, is never red in the saveContact native methods.
Is your feature request related to a problem? Please describe.
Our customers are manually creating leads (contacts) in our app, and we would like to let the user have the option to save that lead into their phone's contacts.
Describe the solution you'd like
Camera.saveContact()
Describe alternatives you've considered
/
Additional context
With some direction and help, we would contribute that feature by ourselves.
Describe the bug
I have updated the plugin from v2.0.0 to v4.0.0. While testing on both platforms, I have noticed that the checkPermissions()
method doesn't do anything on iOS
. Well, at least the native code is called, but I never received a response.
To Reproduce
Steps to reproduce the behavior:
checkPermissions()
method on iOSExpected behavior
I should get a response from the method saying if it's granted, denied, etc...
Screenshots
Logs on iOS:
The response is missing.
Android for example, works correctly, see logs as a demonstration of the expected behavior:
Smartphone:
Additional context
None so far.
Describe the bug
When loading in contacts, some of their already present fields are missing. Fields such as birthday date are returned undefined, even though the contact has that saved on the contact card.
This is happening for many contacts.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
To see all fields present on the contact card.
Screenshots
If applicable, add screenshots to help explain your problem.
Smartphone (please complete the following information):
There is no mention of writing a contact in Readme and typescript didn't give me any clues.
Readme, however, mentions writing in Android permissions.
Thank you.
Describe the bug
Hello.
On android Contacts.getPermissions returns object with a shape {"contacts": "granted" } instead of { granted: true } on first call(e.g. when user did not give the permissions yet), but { granted: true } on the subsequent calls.
I believe that the root of evil is here, and it looks like #40 is going to fix it.
To Reproduce
import { Contacts } from "@capacitor-community/contacts";
const perm = await Contacts.getPermissions();
Expected behavior
Should always return { granted: true|false }
Screenshots
Smartphone (please complete the following information):
Hi sir, can you add a call feature?
Describe the bug
The docs say there's a requestPermissions() method, but I can't seem to see a PluginMethod in the android source for this method? I was looking for it to be sure of what the behavior was as we're migrating from the old @ionic-native/contacts plugin
I'm using Capacitor 4, and therefore v4-beta of this package
Describe the bug
The contacts plugin define request code as 1, but the FingerPrint Cordova plugin also uses this code, so If in my project I have both plugins, then the Fingerprint will fail, because it delegates the onResult android method to our plugin, is there any chance to change the request code?
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Both plugins should work together.
Describe the bug
When request permissions using Contacts.getPermissions()
the app works on iOS but crashes immediately on Android.
To Reproduce
Steps to reproduce the behavior:
npx cap copy;npx cap sync;ionic cap sync;ionic cap copy android;npx cap open android
Contacts.getPermissions()
At this point, the app crashes though the permissions request dialog does popup.
Expected behavior
Request permissions dialog is presented over the running app, Allow
is clicked, contacts are retreived and the Ionic select picker is presented with the list of contacts.
Screenshots
Can provide a video, if it would help.
Desktop (please complete the following information):
Smartphone (please complete the following information):
Additional context
I have added the required entries to the AndroidManifest but the app crashes when asking for permissions to contacts. A confirmation dialog would popup asking the user to allow access to the contacts but the app would crash immediately.
2021-01-21 23:15:55.285 10056-10486/com.puretrim.app E/Capacitor: Serious error executing plugin
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$1.run(Bridge.java:542)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord{93554ff 10056:com.puretrim.app/u0a157} (pid=10056, uid=10157) requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS
at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
at android.os.Parcel.createException(Parcel.java:2357)
at android.os.Parcel.readException(Parcel.java:2340)
at android.os.Parcel.readException(Parcel.java:2282)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5702)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:6813)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2930)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2481)
at android.content.ContentResolver.query(ContentResolver.java:1167)
at android.content.ContentResolver.query(ContentResolver.java:1115)
at android.content.ContentResolver.query(ContentResolver.java:1071)
at ch.byrds.capacitor.contacts.Contacts.getContacts(Contacts.java:99)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$1.run(Bridge.java:542)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.getContentProviderImpl(ActivityManagerService.java:7129)
at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:7568)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2381)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2861)
at android.os.Binder.execTransactInternal(Binder.java:1154)
2021-01-21 23:15:55.290 10056-10056/com.puretrim.app D/Capacitor: App paused
2021-01-21 23:15:55.296 10056-10486/com.puretrim.app E/AndroidRuntime: FATAL EXCEPTION: CapacitorPlugins
Process: com.puretrim.app, PID: 10056
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.getcapacitor.Bridge$1.run(Bridge.java:551)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$1.run(Bridge.java:542)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord{93554ff 10056:com.puretrim.app/u0a157} (pid=10056, uid=10157) requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS
at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
at android.os.Parcel.createException(Parcel.java:2357)
at android.os.Parcel.readException(Parcel.java:2340)
at android.os.Parcel.readException(Parcel.java:2282)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5702)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:6813)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2930)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2481)
at android.content.ContentResolver.query(ContentResolver.java:1167)
at android.content.ContentResolver.query(ContentResolver.java:1115)
at android.content.ContentResolver.query(ContentResolver.java:1071)
at ch.byrds.capacitor.contacts.Contacts.getContacts(Contacts.java:99)
at java.lang.reflect.Method.invoke(Native Method)
at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:99)
at com.getcapacitor.Bridge$1.run(Bridge.java:542)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.getContentProviderImpl(ActivityManagerService.java:7129)
at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:7568)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2381)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2861)
at android.os.Binder.execTransactInternal(Binder.java:1154)
2021-01-21 23:15:55.363 10056-10486/com.puretrim.app I/Process: Sending signal. PID: 10056 SIG: 9
If I manually give the app Contacts permissions, nothing happens. The app does not crash nor does it actually trigger the API to grab the contacts.
Here is the code:
async doSelectContact() {
const { Contacts } = Plugins;
Contacts.getPermissions()
this.picker.buttons = [
{
text: 'Cancel',
role: 'cancel'
},
{
text: 'Confirm',
handler: (value) => {
this.doAddContact(value)
}
}
]
if (this.contacts.length <= 0) {
await Contacts.getContacts().then(async (contacts) => {
await contacts.contacts.forEach(contact => {
if (contact.phoneNumbers.length >= 1) {
this.contacts.push({
text: contact.displayName,
value: contact.phoneNumbers[0]
})
}
this.picker.columns = [{
name: 'Contacts',
options: this.contacts
}]
this.picker.open = true
})
});
}
},
Is your feature request related to a problem? Please describe.
n/a
Describe the solution you'd like
An array of linked contact photos in the result.
Describe alternatives you've considered
The main contact photo as string.
Additional context
n/a
Describe the bug
According to the docs here, if we use:
const {contacts} = await Contacts.getContacts({
projection: {
name: true // <======
}
});
It will only return two properties: contactId and name. This is not what is happening, I am receiving more fields, phones.
Example:
[
{
"contactId": "1",
"name": {
"display": "Juan Perez",
"given": "Juan",
"family": "Perez"
},
"phones": [
{
"type": "mobile",
"number": "(809) 333-2222"
},
{
"type": "custom",
"label": "Flote",
"number": "(849) 333-2222"
}
]
},
]
The same thing is happening with the pickContact()
method. Example:
const result = await Contacts.pickContact({
projection: {
name: true
}
});
Platforms
iOS seems to be fine, but maybe you can re-validate the issue on iOS to be sure is not happening.
To Reproduce
Steps to reproduce the behavior:
getContacts()
method with a projection with only namepickContact()
method with a projection with only nameExpected behavior
Contacts should have only a name object, no more than that.
Example:
[
{
"contactId": "1",
"name": {
"display": "Juan Perez",
"given": "Juan",
"family": "Perez"
},
},
]
Screenshots
None.
Smartphone (please complete the following information):
Additional context
None.
@capacitor-community/contacts : "^1.0.9"
@capacitor/core: "^3.3.2",
I m using Contacts plugin to access user phone contacts. It needs to call Contacts.getPermissions()
function to grant access. If user already granted this permission, i don't want to call getPermissions() function again.
Is there any variable which keep a state of current device if it already granted access ?
Is there any way to detect changes in contacts? , Need to re sync contacts when changes made in contacts!
I followed all the steps in the docs.
But when importing the Contacts class like described in the docs:
import ch.byrds.capacitor.contacts.Contacts;
I received the following error: Cannot resolve symbol ch
As a solution I added an extra random character to the gradle file and clicked the sync button that then appears
Then I removed the random character and synced again.
This made the error disappear.
I am not sure if this is an error with Capacitor itself (i tried running cap update
, cap sync
and cap copy
but none helped) or something else
Hi @Idrimi, I don't think we've been introduced. Would you be able to join the Capacitor Contributors slack? I'd like to add you to our channel if that's alright.
I also have a few questions (we can chat in Slack):
Cannot install v1.0.8.
I need to install this version via npm to support Capacitor 2, but getting an error that this package does not exist.
Describe the bug
Could this be a Bug?
Expected behavior
I shouldn't be getting any errors.
Desktop (please complete the following information):
Smartphone (please complete the following information):
Additional context
none
Describe the bug
When upgrading from Capacitor 3 to Capacitor 4, npm install
fails with the message:
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: @capacitor/[email protected]
npm ERR! node_modules/@capacitor/core
npm ERR! @capacitor/core@"4.0.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @capacitor/core@"^3.0.0" from @capacitor-community/[email protected]
npm ERR! node_modules/@capacitor-community/contacts
npm ERR! @capacitor-community/contacts@"^1.1.3" from the root project
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Install should not return any dependency error.
Additional context
Capacitor 4 has been released recently, see releases here. The peerDependencies
of this plugin need an update.
i used this code in my angular project then i convert my project into capacitor then i use this npm and run code in android studio then adroid throws error error: cannot find symbol
this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
^
symbol: method init(Bundle,<anonymous ArrayList<Class<? extends Plugin>>>)
my java main activity code is package flycard.app;
import ch.byrds.capacitor.contacts.Contacts;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Plugin;
import java.util.ArrayList;
public class MainActivity extends BridgeActivity {
@OverRide
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Initializes the Bridge
this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
// Additional plugins you've installed go here
// Ex: add(TotallyAwesomePlugin.class);
add(Contacts.class);
}});
}
}
When saving a contact that contains emails, phone numbers, or addresses, the contact is saved with a custom type and not with android's own constants, for example TYPE_HOME, TYPE_WORK, etc.
In addition to this, if a website is sent, the information is not saved and in the case of the address it assumes that street, city, state, zip code, country is sent.
Given this, I made changes to the Contacts.java file that allows you to save the contact information in a better way, I hope it will help you. I show places where I made the changes:
`import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public void saveContact(PluginCall call) throws JSONException {
// TODO: allow more than these three email types
if (emailAddresses.size() > 0) {
JSObject primaryEmail = JSObject.fromJSONObject((JSONObject) emailAddresses.get(0));
intent.putExtra(ContactsContract.Intents.Insert.EMAIL, primaryEmail.getString("address", ""));
intent.putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE, primaryEmail.getInteger("label", Email.TYPE_HOME));
intent.putExtra(ContactsContract.Intents.Insert.EMAIL_ISPRIMARY, true);
if (emailAddresses.size() > 1) {
JSObject secondaryEmail = JSObject.fromJSONObject((JSONObject) emailAddresses.get(1));
intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_EMAIL, secondaryEmail.getString("address", ""));
intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE, secondaryEmail.getInteger("label", Email.TYPE_HOME));
if (emailAddresses.size() > 2) {
JSObject tertiaryEmail = JSObject.fromJSONObject((JSONObject) emailAddresses.get(2));
intent.putExtra(ContactsContract.Intents.Insert.TERTIARY_EMAIL, tertiaryEmail.getString("address", ""));
intent.putExtra(ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE, tertiaryEmail.getInteger("label", Email.TYPE_HOME));
}
}
}
// TODO: allow more than these three phone number types
if (phoneNumbers.size() > 0) {
JSObject primaryNumber = JSObject.fromJSONObject((JSONObject) phoneNumbers.get(0));
intent.putExtra(ContactsContract.Intents.Insert.PHONE, primaryNumber.getString("number", ""));
intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, primaryNumber.getInteger("label", Phone.TYPE_HOME));
intent.putExtra(ContactsContract.Intents.Insert.PHONE_ISPRIMARY, true);
if (phoneNumbers.size() > 1) {
JSObject secondaryNumber = JSObject.fromJSONObject((JSONObject) phoneNumbers.get(1));
intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_PHONE, secondaryNumber.getString("number", ""));
intent.putExtra(ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE, secondaryNumber.getInteger("label", Phone.TYPE_HOME));
if (phoneNumbers.size() > 2) {
JSObject tertiaryNumber = JSObject.fromJSONObject((JSONObject) phoneNumbers.get(2));
intent.putExtra(ContactsContract.Intents.Insert.TERTIARY_PHONE, tertiaryNumber.getString("number", ""));
intent.putExtra(ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE, tertiaryNumber.getInteger("label", Phone.TYPE_HOME));
}
}
}
// TODO: allow more than one address
if (addresses.size() > 0) {
JSObject address = JSObject.fromJSONObject((JSONObject) addresses.get(0));
JSObject postalObject = address.getJSObject("address", new JSObject());
ArrayList<String> postalArray = new ArrayList<String>();
postalArray.add(postalObject.getString("street", ""));
postalArray.add(postalObject.getString("city", ""));
postalArray.add(postalObject.getString("state", ""));
postalArray.add(postalObject.getString("postalCode", ""));
postalArray.add(postalObject.getString("country", ""));
postalArray.removeAll(Arrays.asList("", null));
String postal = TextUtils.join(", ", postalArray);
intent.putExtra(ContactsContract.Intents.Insert.POSTAL, postal);
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, address.getInteger("label", StructuredPostal.TYPE_HOME));
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_ISPRIMARY, true);
}
// UrlAddresses
JSArray urlAddressesArray = call.getArray("urlAddresses", new JSArray());
List<Object> urlAddresses = urlAddressesArray.toList();
if (urlAddresses.size() > 0) {
ArrayList<ContentValues> data = new ArrayList<ContentValues>();
ContentValues rowUrlAddress = new ContentValues();
JSObject urlAddress = JSObject.fromJSONObject((JSONObject) urlAddresses.get(0));
rowUrlAddress.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
rowUrlAddress.put(ContactsContract.CommonDataKinds.Website.URL, urlAddress.getString("url", ""));
rowUrlAddress.put(ContactsContract.CommonDataKinds.Website.TYPE, urlAddress.getInteger("label", Website.TYPE_HOME));
data.add(rowUrlAddress);
intent.putExtra(ContactsContract.Intents.Insert.DATA, data);
}
}`
export interface PhoneNumber { label?: number; number?: string; } export interface EmailAddress { label?: number; address?: string; } export interface PostalAddress { label?: number; address?: { street?: string; city?: string; state?: string; postalCode?: string; country?: string; }; }
Result with the changes applied in Contacts.java:
Describe the bug
The plugin does not add middleName
in contact displayName
To Reproduce
Steps to reproduce the behavior:
diaplayName
displayName
does not include middleName
for contacts where it existsExpected behavior
The displayName should includegivenName
,middleName
, and famillyName
Screenshots
Desktop (please complete the following information):
Smartphone (please complete the following information):
Additional context
Change line 68
in Plugin.swift to "displayName": "\(contact.givenName) \(contact.middleName) \(contact.familyName)",
should fix the issue.
There is a way to input the physical address of contact. However, according to the Interface there is no way to retrieve it.
Describe the solution you'd like
Add Address with an Address Interface
Describe alternatives you've considered
There is no alternative
Is your feature request related to a problem? Please describe.
I have used the Cordova Contacts plugin for many years and original underestimated how important the labels are for phone numbers. Without them, very little context is offered.
Describe the solution you'd like
I am very novice in swift but managed to add labels to the phoneNumbers array returned from getContacts in iOS with
for number in contact.phoneNumbers {
let numberToAppend = number.value.stringValue
let labelToAppend = number.label?.replacingOccurrences(of: "[!$_<>]", with: "", options: .regularExpression, range: nil)
phoneNumbers[labelToAppend] = numberToAppend
}
That might be horrible practice in Swift but it's working for me.
Describe alternatives you've considered
Without adding the label to each phone number it will be very difficult to know which number needs to be selected in an app using this plugin.
The Capacitor 3 RC is out! ๐ This library should support the new version
Follow the plugin upgrade guide here: https://capacitorjs.com/docs/v3/updating/plugins/3-0
The migration is pretty easy, I've done it for a couple plugins. Happy to assist if you need assisting!
Hi, I'm the founder of FriendApp. Thank you for your great work on this component.
I was wondering if the import process could also read the contact creation date for iPhone and Android? We're going through a lengthy workaround to detect new contacts added by users, and was curious to see if there might be an easier way to handle this is we could read the iphone and Android contact creation date.
Please feel free to download if you'd like to check out our app :)
https://apps.apple.com/us/app/friend/id1599335622
Updating a contact may be considered a fairly basic feature that should probably be implemented. However, it is not as straight forward as one might think.
If you are in need of this functionality, please +1 this post by means of a GitHub reaction (so not with a comment).
Approach 1
The easiest approach would be to implement a PUT instead of PATCH (REST-API analogy). That means that the contact will get completely overwritten with the newly defined values. Essentially, this would be the same as removing the contact and creating a new one.
This is quite easy to implement, but the developer experience, and (maybe moreover) the user experience is sub-optimal to say the least.
If the developer only wants to update a single contact's phone number, he would still have to retrieve all of that user's data, and consequently put all that data into the updateContact
method. Not very ideal.
If the developer forgets to do above said things, a user might end up with a contact with only the newly updated phone number, with all "old" data deleted. Not very user friendly.
Approach 2
It would be possible to do a "partial PUT". This would entail that the developer does not have to supply all data, but just the data for the object he is updating. So if, for example, the developer wants to update a contact's phone number, he will only have to supply all the other phone numbers that are supposed to be kept.
This approach has a better developer experience than approach # 1, but it's still not very ideal.
Approach 3
Arguably the best approach is this: make it possible to retrieve the IDs of a contact's phone, email, postal address etc. This way a developer can just specify which object (phone, email, postal address) object should be updated, by simply providing the correct id.
Still this has some quirks that should be thought about. For example:
The developer wants to updated the postal address with id 123. But he only wants to update the street, not all the other fields... What should be done now? Does the developer have to specify all the other "old" fields, so we can overwrite the whole object. Or does the API only update the given fields. But in the latter case, how would it be possible to delete / empty fields?
Of course, this approach, will also take the longest to develop and test.
All in all, still some questions. Unfortunately, I don't have the time, nor the need, to implement this. But of course, suggestions, comments, PRs, or any input for that matter, is very welcome.
Describe the bug
This plugin @capacitor-community/contacts crashes on Android so I tried the referenced demo app in the README and it does NOT crash. However, the demo app uses a different plug-in @byrds/capacitor-contacts.
Is the source code for these plugins supposed to be identical? I'm confused why both exist and why the one under "capacitor-community" is the one that is crashing. It's also confusing that this plugin points to a demo app at the uses a different plugin.
Describe the bug
App crash on "Deny" permission selection (Android only)
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Catch the failure internally and return { granted: false } from Contacts.getPermissions()
Smartphone (please complete the following information):
Additional context
AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_PROFILE" android:maxSdkVersion="29" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
I have not tested on iOS but Android performance seems to be lacking.
Loading of ~500 contacts takes ~20 to ~30 seconds on a Samsung Galaxy S10.
I am in no way an Android expert. I have very little experience with Android development. But I was able to build a little ContactsPlugin.java class based on stackoverflow sources. And this works way faster (< 1 second). However it does not handle birthday etc. But it does the core job of getting names and phone numbers.
import android.content.ContentResolver;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.util.Log;
import com.getcapacitor.JSArray;
import com.getcapacitor.JSObject;
import com.getcapacitor.NativePlugin;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import java.util.HashSet;
import java.util.Set;
@NativePlugin()
public class ContactsPlugin extends Plugin {
private static String TAG = "CONTACT";
// Get contacts
// Output example:
// { contacts: [{ fullname: 'John Doe', phone: '+31612345678' }] }
@PluginMethod()
public void getContacts(PluginCall call) {
JSArray contacts = new JSArray();
Set<String> uniquePhonenumbers = new HashSet<String>();
ContentResolver contentResolver = getContext().getContentResolver();
Cursor phoneCursor = contentResolver.query(Phone.CONTENT_URI, new String[]{Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER}, null, null, Phone.DISPLAY_NAME + " ASC");
if (phoneCursor != null && phoneCursor.getCount() > 0) {
while (phoneCursor.moveToNext()) {
String fullname = phoneCursor.getString(phoneCursor.getColumnIndex(Phone.DISPLAY_NAME));
String phone = phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER));
if (phone != null && !phone.isEmpty()) {
// remove whitespaces from phone number
// this way we can check for duplicates more accurately
String phoneStripped = phone.replaceAll("\\s+", "");
// check for duplicates
if (!uniquePhonenumbers.contains(phoneStripped)) {
JSObject obj = new JSObject();
obj.put("fullname", fullname);
obj.put("phone", phone);
contacts.put(obj);
// put in hashlist to be able to check for duplicates
uniquePhonenumbers.add(phoneStripped);
Log.d(TAG, "phone:" + phone);
}
}
}
}
JSObject obj = new JSObject();
obj.put("contacts", contacts);
call.resolve(obj);
}
}
Is your feature request related to a problem? Please describe.
On iOS devices, if the contact has a phone number and a business name but no first or last name, the contact returned is simply the phone number. When displaying this information on a contact list selector, it makes for a bad user experience.
Example JSON key value pair object:
{
"phoneNumbers":[
"(000) 000-0000"
],
"contactId":"6AA19AB6-16CF-4325-9E3B-5EDF948AC577:ABPerson",
"displayName":" ",
"emails":[
]
}
Describe the solution you'd like
It would be nice of the business name was returned in the object. The documentation says "organizationName" should exist however, using version 0.1.1 of the plugin, I do not see this key value pair, even empty.
Describe alternatives you've considered
For now, we're going to filter out phone numbers that do not have a first and / or last name.
Once installed, Contact plugin is not available in the "window.Capacitor.Plugins" javascript variable, as all the Core plugins are.
I don't use any bundler, so I can't use the "import" usage recommandation.
How to use this capacitor plugin without import, and like other plugins (for example window.Capacitor.Plugins.SplashScreen.hide()
Thanks
Describe the bug
When running on iOS birthday
, organizationName
and organizationName
are not populated in returned contacts structure.
To Reproduce
Steps to reproduce the behavior:
Contacts.getContacts()
birthday
, organizationName
and organizationName
are missing.Expected behavior
Expecting to have birthday
, organizationName
and organizationName
populated for contacts that have birthday and organization/copany in native contacts app - same as on Android.
Describe the bug
A clear and concise description of what the bug is.
After installing the plugin with npm and syncing with capacitor I'm unable to compile my project in xcode.
The following errors are thrown in the Plugin.swift file:
Invalid conversion from throwing function of type '(Bool) throws -> Void' to non-throwing function type '(Bool) -> Void'
Line 34Consecutive statements on a line must be separated by ';'
Line 87Expected expression
Line 87Expected '}' in class
Line 98To Reproduce
Steps to reproduce the behavior:
Expected behavior
A clear and concise description of what you expected to happen.
I expect it to build.
Screenshots
If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information):
Smartphone (please complete the following information):
Describe the bug
I have read the contacts from the address book in my app. Now, if few changes are made to the contact then is it possible to update the existing contact in the address book?
There is only save method which i understand will just create a new contact rather updating.
Please advise
capacitor 3
โ Updating iOS native dependencies with pod install - failed!
โ update ios - failed!
[error] Analyzing dependencies
[!] No podspec found for CapacitorCommunityContacts
in ../../node_modules/@capacitor-community/contacts
Is your feature request related to a problem? Please describe.
It seems there is no way to delete the contact.
Describe the solution you'd like
I would like to have the ability to delete the contact from address book so that my app can help user keep the address book clean as well.
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Additional context
Add any other context or screenshots about the feature request here.
Extra whitespace is being added in contact name on saveContact()
I tested this with android , extra whitespace is being added in contact name at end.
I think problem is : I am passing only givenName and in plugin I can see there is middle and family name is being concatenated,
so if no value is given for middle and family name it takes 2 whitespaces at end.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Should not add extra spaces in name
I apologise in advance for writing this here, i could find a link to contact the developer direct.
How would i get this contacts plugin to work on a PWA using JavaScript or jQuery ? and is this possible? I'm busy learning about Capacitor. I have searched for days now, but not finding any conclusive information. Hence I'm writing here. A example would would also be appreciated. Greets Tony
Describe the solution you'd like
Getting the contacts sorted by user system preferences is very helpful. it could also be based on a data passed to the plugin call (sort order asc / dec etc..)
Additional context
on android all you have to do is add sortOrder to the query (this gives the same sort order as default contacts app):
String sortOrder = ContactsContract.Contacts.SORT_KEY_PRIMARY + " ASC";
Cursor contactsCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
on ios my implementation is different, here is how I get the contacts and sort them:
//
// ContactsPlugin.swift
// App
//
// Created by Guy Kedar on 2/27/20.
//
import Foundation
import Contacts
import Capacitor
let encoder = JSONEncoder()
var contactsStore: CNContactStore = CNContactStore();
@objc(ContactsPlugin)
public class ContactsPlugin: CAPPlugin {
@objc func authorize(_ call: CAPPluginCall) {
// Check if contacts is available
contactsStore.requestAccess(for: .contacts) {(access, error) in
if !access {
call.reject("Could not get contacts read permission")
}
call.resolve()
}
// Request permission
}
@objc func getContacts(_ call: CAPPluginCall){
let req = CNContactFetchRequest(keysToFetch: [
CNContactFamilyNameKey as CNKeyDescriptor,
CNContactGivenNameKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor,
CNContactThumbnailImageDataKey as CNKeyDescriptor,
CNContactImageDataAvailableKey as CNKeyDescriptor,
CNContactIdentifierKey as CNKeyDescriptor
])
req.sortOrder = CNContactSortOrder.userDefault
var contacts: [[String: Any]] = [];
try! contactsStore.enumerateContacts(with: req) {
contact, stop in
if (!contact.phoneNumbers.isEmpty) {
var numbers: [String] = []
for phoneNumber in contact.phoneNumbers {
numbers.append(phoneNumber.value.stringValue)
}
var customContact: [String: Any] = ["displayName": contact.givenName + " " + contact.familyName, "phoneNumbers": numbers, "contactId": contact.identifier
]
if (contact.imageDataAvailable) {
customContact["image"] = contact.thumbnailImageData?.base64EncodedString()
}
contacts.append(customContact)
}
}
print("## after contacts", contacts.count)
call.resolve([
"contacts": contacts
])
}
}
I am no Java or swift expert, just hacking around using stack overflow but will be happy to contribute.
P.S thanks for this plugin, I had serious perf problems with android and managed to cut read times from 20sec to 200ms by using code from here.
I think generally adding sort and more optional fields like contact images could make this plugin more generic and useful.
Describe the bug
Using the plugin v4.0.1 (latest), the app crashes on iOS if I use a projection with image
.
To Reproduce
Steps to reproduce the behavior:
const {contacts} = await Contacts.getContacts({
projection: {
name: true,
phones: true,
image: true // <=====
}
});
console.log(contacts);
Expected behavior
No crashes.
Smartphone (please complete the following information):
Xcode logs
I don't know if these logs can give you more insight of the issue.
"exception" : {"codes":"0x0000000000000000, 0x0000000000000000","rawCodes":[0,0],"type":"EXC_CRASH","signal":"SIGABRT"},
"asiBacktraces" : ["0 CoreFoundation 0x000000018040e7c8 __exceptionPreprocess + 172\n1 libobjc.A.dylib 0x0000000180051144 objc_exception_throw + 56\n2 CoreFoundation 0x000000018040e6d8 -[NSException initWithCoder:] + 0\n3 Contacts 0x000000018871ffe4 -[CNContact thumbnailImageData] + 144\n4 CapacitorCommunityContacts 0x00000001027d6780 $s26CapacitorCommunityContacts14ContactPayloadC8fillDatayySo9CNContactCF + 5692\n5 CapacitorCommunityContacts 0x00000001027d9eb8 $s26CapacitorCommunityContacts0C0C03getC0ySayAA14ContactPayloadCGAA03GetC15ProjectionInputCFySo9CNContactC_Spy10ObjectiveC8ObjCBoolVGtXEfU_ + 140\n6 CapacitorCommunityContacts 0x00000001027d9f48 $sSo9CNContactCSpy10ObjectiveC8ObjCBoolVGIggy_AbFIeggy_TR + 20\n7 CapacitorCommunityContacts 0x00000001027d9fc8 $sSo9CNContactCSpy10ObjectiveC8ObjCBoolVGIeggy_AbFIyByy_TR + 64\n8 Contacts 0x00000001886cd2cc __74-[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:]_block_invoke_2 + 120\n9 Contacts 0x00000001886cd1e0 __74-[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:]_block_invoke + 212\n10 Contacts 0x00000001886f8878 __91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke_6 + 152\n11 CoreFoundation 0x0000000180376d94 __NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__ + 16\n12 CoreFoundation 0x000000018042f4a8 -[__NSArrayI enumerateObjectsWithOptions:usingBlock:] + 152\n13 Contacts 0x00000001886f87c4 __91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke_5 + 140\n14 ContactsFoundation 0x0000000188a849ac __43-[CNObservable enumerateObjectsUsingBlock:]_block_invoke + 92\n15 ContactsFoundation 0x0000000188ab61b4 -[_CNBlockObserver observerDidReceiveResult:] + 168\n16 ContactsFoundation 0x0000000188ab61b4 -[_CNBlockObserver observerDidReceiveResult:] + 168\n17 ContactsFoundation 0x0000000188ab61b4 -[_CNBlockObserver observerDidReceiveResult:] + 168\n18 ContactsFoundation 0x0000000188ab61b4 -[_CNBlockObserver observerDidReceiveResult:] + 168\n19 ContactsFoundation 0x0000000188ab61b4 -[_CNBlockObserver observerDidReceiveResult:] + 168\n20 Contacts 0x000000018869ea90 __52-[CNXPCDataMapper contactObservableForFetchRequest:]_block_invoke_2 + 116\n21 Contacts 0x000000018869f02c __100-[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke_2 + 76\n22 Contacts 0x000000018867f3d0 __85-[CNiOSABContactBuffersDecoder decodeContactsFromData:moreComing:replyHandler:error:]_block_invoke + 176\n23 Contacts 0x00000001886bd944 -[CNContactBufferDecoder decodeContactsFromBuffer:replyHandler:] + 304\n24 Contacts 0x000000018867f2b8 -[CNiOSABContactBuffersDecoder decodeContactsFromData:moreComing:replyHandler:error:] + 200\n25 Contacts 0x000000018869ef94 __100-[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke + 184\n26 Contacts 0x000000018869f4a0 __91-[CNXPCDataMapper fetchEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke + 256\n27 CoreFoundation 0x0000000180414d00 __invoking___ + 144\n28 CoreFoundation 0x0000000180411ee8 -[NSInvocation invoke] + 276\n29 Foundation 0x0000000180cae608 __NSXPCCONNECTION_IS_CALLING_OUT_TO_REPLY_BLOCK__ + 12\n30 Foundation 0x0000000180cae240 -[NSXPCConnection _decodeAndInvokeReplyBlockWithEvent:sequence:replyInfo:] + 484\n31 Foundation 0x0000000180cb22b0 __88-[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:]_block_invoke_5 + 184\n32 Foundation 0x0000000180cb1dcc -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 2164\n33 Foundation 0x0000000180cb1390 -[NSXPCConnection _sendSelector:withProxy:arg1:arg2:] + 124\n34 Foundation 0x0000000180cc4b20 _NSXPCDistantObjectSimpleMessageSend2 + 64\n35 Contacts 0x000000018869f18c -[CNXPCDataMapper fetchEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:] + 312\n36 Contacts 0x000000018869ec20 -[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:] + 244\n37 Contacts 0x000000018869e97c __52-[CNXPCDataMapper contactObservableForFetchRequest:]_block_invoke + 188\n38 ContactsFoundation 0x0000000188a84088 __26-[CNObservable doOnError:]_block_invoke + 232\n39 ContactsFoundation 0x0000000188a83e50 __31-[CNObservable doOnCompletion:]_block_invoke + 232\n40 ContactsFoundation 0x0000000188a83e50 __31-[CNObservable doOnCompletion:]_block_invoke + 232\n41 ContactsFoundation 0x0000000188a84088 __26-[CNObservable doOnError:]_block_invoke + 232\n42 ContactsFoundation 0x0000000188a83bac __27-[CNObservable doOnCancel:]_block_invoke + 192\n43 ContactsFoundation 0x0000000188a84890 -[CNObservable enumerateObjectsUsingBlock:] + 320\n44 Contacts 0x00000001886f8544 __91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke.55 + 428\n45 Contacts 0x000000018874dbd0 __38-[_CNContactsLogger fetchingContacts:]_block_invoke_2 + 36\n46 libsystem_trace.dylib 0x0000000180065ba8 os_activity_apply_f + 84\n47 Contacts 0x000000018874db40 -[_CNContactsLogger fetchingContacts:] + 188\n48 Contacts 0x00000001886f81d4 -[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:] + 452\n49 Contacts 0x00000001886ccfe8 -[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:] + 240\n50 Contacts 0x00000001886ccd98 -[CNContactStore enumerateContactsWithFetchRequest:error:usingBlock:] + 464\n51 CapacitorCommunityContacts 0x00000001027d9c84 $s26CapacitorCommunityContacts0C0C03getC0ySayAA14ContactPayloadCGAA03GetC15ProjectionInputCF + 420\n52 CapacitorCommunityContacts 0x00000001027ddf1c $s26CapacitorCommunityContacts0C6PluginC03getC0yySo13CAPPluginCallCF + 420\n53 CapacitorCommunityContacts 0x00000001027de20c $s26CapacitorCommunityContacts0C6PluginC03getC0yySo13CAPPluginCallCFTo + 52\n54 Capacitor 0x000000010299a208 $s9Capacitor0A6BridgeC12handleJSCall4callyAA0D0V_tFyycfU_ + 796\n55 Capacitor 0x000000010298e7c8 $sIeg_IeyB_TR + 48\n56 libdispatch.dylib 0x0000000180133fa4 _dispatch_call_block_and_release + 24\n57 libdispatch.dylib 0x0000000180135768 _dispatch_client_callout + 16\n58 libdispatch.dylib 0x000000018013d7f8 _dispatch_lane_serial_drain + 780\n59 libdispatch.dylib 0x000000018013e43c _dispatch_lane_invoke + 392\n60 libdispatch.dylib 0x000000018014a624 _dispatch_workloop_worker_thread + 772\n61 libsystem_pthread.dylib 0x00000001b059a8fc _pthread_wqthread + 284\n62 libsystem_pthread.dylib 0x00000001b05996c0 start_wqthread + 8"],
"extMods" : {"caller":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"system":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"targeted":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"warnings":0},
"lastExceptionBacktrace" : [{"imageOffset":1185724,"symbol":"__exceptionPreprocess","symbolLocation":160,"imageIndex":1},{"imageOffset":151876,"symbol":"objc_exception_throw","symbolLocation":56,"imageIndex":13},{"imageOffset":1185496,"symbol":"-[NSException initWithCoder:]","symbolLocation":0,"imageIndex":1},{"imageOffset":692196,"symbol":"-[CNContact thumbnailImageData]","symbolLocation":144,"imageIndex":17},{"imageOffset":26496,"sourceLine":155,"sourceFile":"ContactPayload.swift","symbol":"ContactPayload.fillData(_:)","imageIndex":18,"symbolLocation":5692},{"imageOffset":40632,"sourceLine":93,"sourceFile":"Contacts.swift","symbol":"closure #1 in Contacts.getContacts(_:)","imageIndex":18,"symbolLocation":140},{"imageOffset":40776,"sourceFile":"<compiler-generated>","symbol":"thunk for @callee_guaranteed (@guaranteed CNContact, @unowned UnsafeMutablePointer<ObjCBool>) -> ()","symbolLocation":20,"imageIndex":18},{"imageOffset":40904,"sourceFile":"<compiler-generated>","symbol":"thunk for @escaping @callee_guaranteed (@guaranteed CNContact, @unowned UnsafeMutablePointer<ObjCBool>) -> ()","symbolLocation":64,"imageIndex":18},{"imageOffset":352972,"symbol":"__74-[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:]_block_invoke_2","symbolLocation":120,"imageIndex":17},{"imageOffset":352736,"symbol":"__74-[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:]_block_invoke","symbolLocation":212,"imageIndex":17},{"imageOffset":530552,"symbol":"__91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke_6","symbolLocation":152,"imageIndex":17},{"imageOffset":564628,"symbol":"__NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__","symbolLocation":16,"imageIndex":1},{"imageOffset":1320104,"symbol":"-[__NSArrayI enumerateObjectsWithOptions:usingBlock:]","symbolLocation":152,"imageIndex":1},{"imageOffset":530372,"symbol":"__91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke_5","symbolLocation":140,"imageIndex":17},{"imageOffset":162220,"symbol":"__43-[CNObservable enumerateObjectsUsingBlock:]_block_invoke","symbolLocation":92,"imageIndex":19},{"imageOffset":364980,"symbol":"-[_CNBlockObserver observerDidReceiveResult:]","symbolLocation":168,"imageIndex":19},{"imageOffset":364980,"symbol":"-[_CNBlockObserver observerDidReceiveResult:]","symbolLocation":168,"imageIndex":19},{"imageOffset":364980,"symbol":"-[_CNBlockObserver observerDidReceiveResult:]","symbolLocation":168,"imageIndex":19},{"imageOffset":364980,"symbol":"-[_CNBlockObserver observerDidReceiveResult:]","symbolLocation":168,"imageIndex":19},{"imageOffset":364980,"symbol":"-[_CNBlockObserver observerDidReceiveResult:]","symbolLocation":168,"imageIndex":19},{"imageOffset":162448,"symbol":"__52-[CNXPCDataMapper contactObservableForFetchRequest:]_block_invoke_2","symbolLocation":116,"imageIndex":17},{"imageOffset":163884,"symbol":"__100-[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke_2","symbolLocation":76,"imageIndex":17},{"imageOffset":33744,"symbol":"__85-[CNiOSABContactBuffersDecoder decodeContactsFromData:moreComing:replyHandler:error:]_block_invoke","symbolLocation":176,"imageIndex":17},{"imageOffset":289092,"symbol":"-[CNContactBufferDecoder decodeContactsFromBuffer:replyHandler:]","symbolLocation":304,"imageIndex":17},{"imageOffset":33464,"symbol":"-[CNiOSABContactBuffersDecoder decodeContactsFromData:moreComing:replyHandler:error:]","symbolLocation":200,"imageIndex":17},{"imageOffset":163732,"symbol":"__100-[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke","symbolLocation":184,"imageIndex":17},{"imageOffset":165024,"symbol":"__91-[CNXPCDataMapper fetchEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]_block_invoke","symbolLocation":256,"imageIndex":17},{"imageOffset":1211648,"symbol":"__invoking___","symbolLocation":144,"imageIndex":1},{"imageOffset":1199848,"symbol":"-[NSInvocation invoke]","symbolLocation":276,"imageIndex":1},{"imageOffset":5891592,"symbol":"__NSXPCCONNECTION_IS_CALLING_OUT_TO_REPLY_BLOCK__","symbolLocation":12,"imageIndex":7},{"imageOffset":5890624,"symbol":"-[NSXPCConnection _decodeAndInvokeReplyBlockWithEvent:sequence:replyInfo:]","symbolLocation":484,"imageIndex":7},{"imageOffset":5907120,"symbol":"__88-[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:]_block_invoke_5","symbolLocation":184,"imageIndex":7},{"imageOffset":5905868,"symbol":"-[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:]","symbolLocation":2164,"imageIndex":7},{"imageOffset":5903248,"symbol":"-[NSXPCConnection _sendSelector:withProxy:arg1:arg2:]","symbolLocation":124,"imageIndex":7},{"imageOffset":5983008,"symbol":"_NSXPCDistantObjectSimpleMessageSend2","symbolLocation":64,"imageIndex":7},{"imageOffset":164236,"symbol":"-[CNXPCDataMapper fetchEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]","symbolLocation":312,"imageIndex":17},{"imageOffset":162848,"symbol":"-[CNXPCDataMapper fetchAndDecodeEncodedContactsForFetchRequest:error:cancelationToken:batchHandler:]","symbolLocation":244,"imageIndex":17},{"imageOffset":162172,"symbol":"__52-[CNXPCDataMapper contactObservableForFetchRequest:]_block_invoke","symbolLocation":188,"imageIndex":17},{"imageOffset":159880,"symbol":"__26-[CNObservable doOnError:]_block_invoke","symbolLocation":232,"imageIndex":19},{"imageOffset":159312,"symbol":"__31-[CNObservable doOnCompletion:]_block_invoke","symbolLocation":232,"imageIndex":19},{"imageOffset":159312,"symbol":"__31-[CNObservable doOnCompletion:]_block_invoke","symbolLocation":232,"imageIndex":19},{"imageOffset":159880,"symbol":"__26-[CNObservable doOnError:]_block_invoke","symbolLocation":232,"imageIndex":19},{"imageOffset":158636,"symbol":"__27-[CNObservable doOnCancel:]_block_invoke","symbolLocation":192,"imageIndex":19},{"imageOffset":161936,"symbol":"-[CNObservable enumerateObjectsUsingBlock:]","symbolLocation":320,"imageIndex":19},{"imageOffset":529732,"symbol":"__91-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]_block_invoke.55","symbolLocation":428,"imageIndex":17},{"imageOffset":879568,"symbol":"__38-[_CNContactsLogger fetchingContacts:]_block_invoke_2","symbolLocation":36,"imageIndex":17},{"imageOffset":19368,"symbol":"os_activity_apply_f","symbolLocation":84,"imageIndex":20},{"imageOffset":879424,"symbol":"-[_CNContactsLogger fetchingContacts:]","symbolLocation":188,"imageIndex":17},{"imageOffset":528852,"symbol":"-[CNDataMapperContactStore enumerateContactsAndMatchInfoWithFetchRequest:error:usingBlock:]","symbolLocation":452,"imageIndex":17},{"imageOffset":352232,"symbol":"-[CNContactStore pool_enumerateContactsWithFetchRequest:error:usingBlock:]","symbolLocation":240,"imageIndex":17},{"imageOffset":351640,"symbol":"-[CNContactStore enumerateContactsWithFetchRequest:error:usingBlock:]","symbolLocation":464,"imageIndex":17},{"imageOffset":40068,"sourceLine":90,"sourceFile":"Contacts.swift","symbol":"Contacts.getContacts(_:)","imageIndex":18,"symbolLocation":420},{"imageOffset":57116,"sourceLine":129,"sourceFile":"ContactsPlugin.swift","symbol":"ContactsPlugin.getContacts(_:)","imageIndex":18,"symbolLocation":420},{"imageOffset":57868,"sourceFile":"<compiler-generated>","symbol":"@objc ContactsPlugin.getContacts(_:)","symbolLocation":52,"imageIndex":18},{"imageOffset":90632,"sourceLine":506,"sourceFile":"CapacitorBridge.swift","symbol":"closure #1 in CapacitorBridge.handleJSCall(call:)","imageIndex":21,"symbolLocation":796},{"imageOffset":42952,"sourceFile":"<compiler-generated>","symbol":"thunk for @escaping @callee_guaranteed () -> ()","symbolLocation":48,"imageIndex":21},{"imageOffset":8100,"symbol":"_dispatch_call_block_and_release","symbolLocation":24,"imageIndex":14},{"imageOffset":14184,"symbol":"_dispatch_client_callout","symbolLocation":16,"imageIndex":14},{"imageOffset":47096,"symbol":"_dispatch_lane_serial_drain","symbolLocation":780,"imageIndex":14},{"imageOffset":50236,"symbol":"_dispatch_lane_invoke","symbolLocation":392,"imageIndex":14},{"imageOffset":99876,"symbol":"_dispatch_workloop_worker_thread","symbolLocation":772,"imageIndex":14},{"imageOffset":14588,"symbol":"_pthread_wqthread","symbolLocation":284,"imageIndex":8},{"imageOffset":9920,"symbol":"start_wqthread","symbolLocation":8,"imageIndex":8}],
Additional context
If I remove the image projection, all works as expected.
@Idrimi @muuvmuuv @rdlabo @dotNetkow
First of all thank you for everything you guys have done to make this plugin available for the capacitor community. ๐ช
I have noticed maintenance of this plugin has somewhat stalled. Maybe the interest of the maintainer has shifted, or maybe they simply lack the time. Which is totally fair of course. Any work done for the open source community is just an extra, especially when it's done without any compensation at all. ๐๏ธ
I was in need of a few (yet) unimplemented features. Recently I started adding those features to this plugin in a private fork. Eventually things escalated a bit, and I ended up refactoring a lot and finally I just wrote everything from scratch again. Haha.
I like to share my work with the capacitor community. It will close all but one open issues. I have written the plugin to support Capacitor v3, but I will also apply the necessary changes to support v4. That way both versions of Capacitor can be supported with the exact same plugin API.
I could just create a new repo and npm package. But I think it's more desirable to not create a bunch of different repositories every time a new maintainers stands up and refactors a bunch of stuff. So therefore I am requesting the permission of you guys to mostly take over the maintainership of this plugin and release a v3 (unfortunately a v2 has just been shipped, so these will be following each other shortly, but oh well) and a v4 versions of the plugin.
Sidenote: I already have the necessary permissions and roles for this repository and npm, so you do not need to do anything in case you guys agree @dotNetkow ๐
When trying to run my app with this plugin as well as the example application (https://github.com/byrdsandbytes/capContactsDemo) i get the following errors when running 'ionic capacitor run android'.
Running Gradle build - failed!
[capacitor] [error]
I ran the following commands before trying to run the application on android.
In the documentation we are supposed to import this plugin like so:
import { Plugins } from "@capacitor/core";
const { Contacts } = Plugins;
This method of importing plugins will be removed in version 4 of capacitor and is currently deprecated in v3. Other plugins, such as the geolocation plugin (https://capacitorjs.com/docs/apis/geolocation) have made this change like so:
import { Geolocation } from '@capacitor/geolocation';
Just bringing this to people's attention as something that will need to be fixed for this plugin to work in version 4.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.