How to get users by address book - ios

I'm trying to develop an iPhone app and implementing a functionality similar to snapchat's and so many other apps. I want to ask for the user's mobile phone number and add it to my database. I then want to ask for permission to access my address book and based on my mobile phone contacts (friends mobile number) I can suggest the profiles of my friends who have installed the app. I developed some functions in which I was able to get all the user's contacts as a string. But I have some issues. People can write the mobile numbers in different ways (putting the country code in first place, etc). Since I am matching an exact string it becomes kind of hard.
Is there any solution for this problem? I know that different countries have different ways of writing the mobile numbers.
F.e. in Portugal we can write 00351 911 111 111 or +351 911 111 111 or 911 111 111 . This will be easy to solve in Portugal since we all have the last 9 digits and it is easy to compare. But in the US I have seen (555) XXX-XXXX or XXX-XXX-XXXX and many other formats.. I can't determine how the user has saved the contact and the same with other countries.
ALL HAPPENS AFTER ASKING FOR AN AUTHORIZATION
In the viewDidLoad I search in Parse the users of my mobile phone
override func viewDidAppear(animated: Bool) {
var query = PFUser.query()
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let users = objects {
self.contactsUsernameArray.removeAll(keepCapacity: false)
self.contactsNameArray.removeAll(keepCapacity: false)
self.contactsImageFiles.removeAll(keepCapacity: false)
self.contactsNumberArray.removeAll(keepCapacity: false)
for object in users {
for var i = 0; i < self.phoneNumbers.count - 1; i++ {
if object["phoneNumber"] as! String == self.phoneNumbers[i] {
self.contactsUsernameArray.append(object.username!!)
self.contactsNameArray.append(object["name"] as! String)
self.contactsNumberArray.append(object["phoneNumber"] as! String)
self.contactsImageFiles.append(object["photo"] as! PFFile)
}
}
}
}
//println(self.usernames)
//println(self.userids)
self.contactsTable.reloadData()
})
}

You can try phone numbers format E.164
E.164

Related

Does unifiedContacts(matching:keysToFetch:) return only unified contacts and leave out non-unified contacts?

When I use the Contacts Framework with Swift for iOS 15 using Xcode 13, when I use CNContactStore instance method unifiedContacts(matching:keysToFetch:), what happens to those CNContact objects that are not linked with any other CNContact objects, would they be returned as non-unified contacts, or would they be left out since they are non-unified contacts?
NOTE: I made an error in the original post before I fixed it. I meant to ask about unifiedContacts (with an "s").
Instead I accidentally wrote unifiedContact (without the "s").
The sample iOS Swift project ManagingContacts, uses both enumerateContacts(with:usingBlock:) and unifiedContacts(withIdentifier:keysToFetch:). Understanding why they choose to use each when they do may help answer the question.
The name "unifiedContacts" suggests it only fetches unified contacts, and does not fetch non-unified contacts that otherwise would meet the specified criteria, but it doesn't make sense to me why there would ever be a need for it or why it is used in the said project when it is used.
From my test code, it looks like they both do the same thing. What do you think about this?
I tried this code in a test project:
class ViewController: UIViewController {
let store = CNContactStore()
let keys = [CNContactGivenNameKey as CNKeyDescriptor]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
store.requestAccess(for: .contacts) {
permissionGranted, error in
let request = CNContactFetchRequest(keysToFetch: self.keys)
print("enumerate contacts")
do {
try self.store.enumerateContacts(with: request) {
contact, pointer in
print(contact.givenName)
}
} catch {
print("error")
print(error.localizedDescription)
}
print("unified contacts")
do {
let containers = try self.store.containers(matching: nil)
if containers.count > 1 {
fatalError("More than one container!")
}
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: self.store.defaultContainerIdentifier())
let contacts = try self.store.unifiedContacts(matching: predicate, keysToFetch: self.keys)
for contact in contacts {
print(contact.givenName)
}
} catch {
print("error")
print(error.localizedDescription)
}
}
}
}
Here's the result in the debug window:
enumerate contacts
Kate
Daniel
John
Anna
Hank
David
unified contacts
Kate
Daniel
John
Anna
Hank
David
enumerateContacts will go over every contact that matches the fetch request that you pass to it. The block will be called once for each contact.
unifiedContact is a function that receives the identifier of a contact and, if there are multiple cards for that contact, will give you a new contact that aggregates the values of all of those cards. If there is a single card then that's what will be returned.
You would probably run enumeracteContacts and then within the enumeration block you would call unifiedContact to make sure you get all the relevant values for that contact.
Edit:
In your updated question you're actually using a different unifiedContact function than the one in your original question. This function returns an array of unified contacts.
Your examples look the same because that is a basic address book and no contacts are linked. When you link two or more contacts you could have more than one contact card for a single contact, and unifiedContacts functions will return a single one with all the values.
By default the Contacts framework returns unified contacts. You are absolutely right. The name "unifiedContacts" suggests it only fetches unified contacts, and does not fetch non-unified contacts... Other methods should be used to retrieve non-unified contacts.

Best way to show realtime data in iOS with Charts API

I know there are some posts about it but I can't find something useful. Therefore, I'm opening new post. I have a device which sends some values over bluetooth. I have function which gets this value like every second. I need to show this data in line chart realtime (Two line). I know Android API has a function for realtime but I can't find it in iOS API.
How I'm doing now is like this;
if (uuid == kUUIDECGSensor) {
let dataDict: [AnyHashable: Any] = LBValueConverter.manageValueElectrocardiography(dataValue)
// print("kUUIDECGSensor dict: \(dataDict)")
let allValues = Array(dataDict.values)
print(allValues)
data_array1.append(allValues[0] as! Double)
data_array2.append(allValues[1] as! Double)
//print(data_array1)
//print(data_array2)
temp_time += 5
time.append("\(temp_time)")
setChart(dataPoints: time, values: data_array1)
}
It is working but It doesnt seem like realtime. What am I missing?
Thank You!

Spotify iOS SDK returning songs that do not exist? - Swift

So I'm building some playlist and song retrieval into my app at the moment, and I'm really confused by some of the results I'm getting back from the API. It seems to be returning songs that no longer exist on Spotify or have been long removed from a playlist.
Retrieving a list of Playlists from a user is working fine, but just in case this problem is arising from the way I draw that playlist's tracks, here is the code I use to get them:
SPTPlaylistSnapshot.playlistWithURI(uri, accessToken: session.accessToken) { (error, playlistSnapshotOb) -> Void in
if let playlistSnapshot = playlistSnapshotOb as? SPTPlaylistSnapshot {
let itemz = playlistSnapshot.firstTrackPage.items //tracksForPlayback()
for item in itemz{
let track = item as! SPTPlaylistTrack
let splice = "\(track.uri)"
let trackURI = splice.stringByReplacingOccurrencesOfString("spotify:track:", withString: "")
var displayArtist = String()
let artistz = track.artists
if artistz.count > 1{
for i in 0...(artistz.count - 1){
let itz = artistz[i] as! SPTPartialArtist
if i > 0 {
displayArtist += ", \(itz.name)"
}else{
displayArtist += "\(itz.name)"
}
}
self.tracks.append(track.name)
self.ArtistObjects.append(displayArtist)
self.uriS.append(trackURI)
}else{
let singularArtist = artistz[0] as! SPTPartialArtist
displayArtist = singularArtist.name
self.tracks.append(track.name)
self.ArtistObjects.append(displayArtist)
self.uriS.append(trackURI)
}
Additionally, below is a screenshot of the desktop Spotify app showing the real content of the playlist I am pulling:
Spotify per Desktop
You'll see that the songs "Big Bank Dank" and "Light Day Remix" are not actually on this playlist, but for some reason, on my app below, when I pull this playlist, it has these songs listed:
Spotify In My App
(Apparently I can't post an actual image because of my rep - apologies)
Any idea why it's doing this?
The tracks are probably just not available any longer for some unspecified reason. This is quite common. By default, the Spotify client does not show unavailable tracks in playlists, but in settings there is a toggle you can flip so that they are shown as greyed out instead.
I don't know about iOS SDK, but there should be either an attribute telling you the available markets for the tracks or if it is playable or not, depending on the country of the user being logged in.
This is how it works in the Web API, which should be similar.
https://developer.spotify.com/web-api/track-relinking-guide/

Convert asynchronously large no of GPS data to city/country

Background: I have a list of contacts, which are retrieved on an asynchronous queue from a cloud based database. Once done, I dispatch back to the main queue and show these contacts in a TableView.
Besides names and other details, each contact object has GPS coordinate properties (latitude and longitude). I want to use these GPS coordinates to retrieve the name of the city and country of each contact and update the TableView showing that information in the local language of the device the user has.
Problem: The problem I am trying to overcome is that I have a few hundred contacts. Initially, I used concurrent queues to get the city/country strings. But I paused the app in XCode and realised the disaster of 300+ threads created. So I changed the code running each lookup of the city/country on the same serial queue.
The issue now is that only for about 50 of the contacts the data is updated. I do not know why and not sure even if the queue is serial. Debugging shows there are still 100+ “serial queues” created. I expected one at the time. What am I doing wrong? Thanks
The code for the class, in which I have my TableView is as follows:
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) {
// get or update the contactsList
activeUser.contactsList.getAllContacts()
// once we have the contacts we go back to the main queue
dispatch_async(dispatch_get_main_queue()) {
// and refresh tableview to show the contacts
self.tableView.reloadData()
// now we refresh the locations of the contacts
for index in 0...activeUser.contactsList.listOfContacts.count
{
// set local variables for lat and long
let lat = activeUser.contactsList.listOfContacts[index].latitude
let long = activeUser.contactsList.listOfContacts[index].longitude
// call location service method with completion handler
MyLocationServices().updateLocationToLocalLanguage(lat, longitude: long, completionHandler:
{ (city, country) -> () in
// update contact's details
activeUser.contactsList.listOfContacts[index].city = city
activeUser.contactsList.listOfContacts[index].country = country
// refresh each time the table to show updated contact data
self.tableView.reloadData()
})
}
}
The method within the MyLocationServices class looks as follows:
func updateLocationToLocalLanguageDispatched(latitude: String, longitude: String, completionHandler: (city: String, country: String) -> ())
{
let serialQ = dispatch_queue_create("AddressUpdateQ", DISPATCH_QUEUE_SERIAL)
dispatch_async(serialQ)
{
var cityString = "NA"
var countryString = "NA"
let group = dispatch_group_create()
dispatch_group_enter(group)
let location = CLLocation(latitude: Double(latitude)!, longitude: Double(longitude)!)
self.geocoder.reverseGeocodeLocation(location)
{
(placemarks, error) -> Void in
if let placemarks = (placemarks as [CLPlacemark]!) where placemarks.count > 0
{
let placemark = placemarks[0]
if ((placemark.addressDictionary!["City"]) as? String != nil) { cityString = ((placemark.addressDictionary!["City"]) as? String)! }
if ((placemark.addressDictionary!["Country"]) as? String != nil) { countryString = ((placemark.addressDictionary!["Country"]) as? String)! }
}
dispatch_group_leave(group)
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
dispatch_async(dispatch_get_main_queue())
{
completionHandler(city: cityString, country: countryString)
}
}
}
I have solved the issue and documenting hoping it is helpful in case others face similar problems.
(1) Serial Queue: I have realised serial queue initiation should not happen within the method, but enclose the for-loop. Thus the following code should be removed from the method and put around the for-loop. But this does not solve the fact that I only get about 50 results and nothing for the remaining GPS data of the contacts.
let serialQ = dispatch_queue_create("AddressUpdateQ", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQ)
{ [for index....] }
(2) Location: The second issue is related to Apple's Geocoder, which happens online. Officially, the number of requests are limited. The documentation states that
one "should not send more than one geocoding request per minute".
Applications should be conscious of how they use geocoding. Geocoding
requests are rate-limited for each app, so making too many requests in
a short period of time may cause some of the requests to fail.
Checking the error code I get, it is Error Domain=kCLErrorDomain Code=2. It seems the service is denied and I cannot make further requests.
(3) Solutions: There are two solutions in my view. One is to update 30 or so users, wait some time, and then dispatch after that another queue to get again some data. The second solution is to live with having all data in English, make a string that each user saves in the cloud. So for other users retrieving contacts, it's just a matter of fetching a string from the database rather than checking GPS data "on the fly".

iOS Make a phone call and pass data

For a Proof of Concept, I'd like to be able to make a phone call to a specific phone number and pass data to the telephone number.
Is this possible?
for your first question I'd like to be able to make a phone call to a specific phone number
Ans:
YES, you can do it like
for ex
let phoneNumber = "1234567890"
if let phoneCallURL = NSURL(string: "telprompt://(phoneNumber)") {
let application = UIApplication.sharedApplication()
if application.canOpenURL(phoneCallURL) {
application.openURL(phoneCallURL)
}
else{
println("failed")
}
}
for your second question pass data to the telephone number,
Ans:
NO, it is not possible
but, you can send the data to backend on that call event, and trigger the sms to particular number

Resources