Identify incoming callers with CallKit when they are already contacts - ios

I am creating an app that tracks money owed between friends. I would like to create a feature where incoming calls will show with a warning if that person owes you money.
I am using the Apple-provided approach to identify callers:
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let labelsKeyedByPhoneNumber: [CXCallDirectoryPhoneNumber: String] = [ … ]
for (phoneNumber, label) in labelsKeyedByPhoneNumber.sorted(by: <) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
context.completeRequest()
}
}
This works great so far for incoming calls. However, if the call is coming from a person that is already in the user's contact list then that name will show and our information will not show.
Is there a way to show our contract information even when the caller is in the user's contact list?

No, data from the CallKit identification extension is only used if there is no match against an existing contact.

Related

Get Current Active user from the Zoom iOS SDK Custom Meeting implementation

I have implemented the Zoom iOS SDK to work with a custom UI. Everything works just as its supposed to but I haven't been able to figure out how I can get the userID of the currently active user.
I have implemented the below delegate method which tells about the current active video user, but unfortunately it shows all the other participants in the meeting except me.
func onSinkMeetingActiveVideo(_ userID: UInt) {
if let service = MobileRTC.shared().getMeetingService(), let username = service.userInfo(byID: userID)?.userName {
print("\(#function) : \(userID) : \(username)")
}
}
I need to know who is the current active user even if its me who is talking.
You can retrieve this kind of information from meeting service MobileRTCMeetingService.
MobileRTCMeetingService
func getActiveUserId() -> UInt? {
if let meetingService = MobileRTC.shared().getMeetingService() {
return meetingService.activeUserID()
}
return nil
}
Extra note: in Zoom there is also the concept of Pinned User that overrides active user in active video cell.
Pinned user id can be retrieved in this way:
func getPinnedUserId() -> UInt? {
if let meetingService = MobileRTC.shared().getMeetingService(), let userList = meetingService.getInMeetingUserList(){
for userId in userList {
if let userId = userId as? UInt, meetingService.isUserPinned(userId) {
return userId
}
}
return nil
}
return nil
}
So in order to establish which is the user id of the video in active video cell you have to check both, giving priority to pinned user.
let currentVideoUserId = getPinnedUserId() ?? getActiveUserId()
During the meeting you will never been the active user in your own video cell because even if your are speaking, you will continue to see the other person in active video cell.
On the other side if you are interested to know who is talking then you have to retrieve the user list and check the audioStatus [MobileRTCAudioStatus].
MobileRTCAudioStatus
MobileRTCMeetingUserInfo
Just pay attention that you can have more than one user speaking at the same time.
There is also another callback that can be useful if you are interested in active speaker user: it is the onSinkMeetingActiveVideoForDeck in MobileRTCVideoServiceDelegate
MobileRTCVideoServiceDelegate
According to the documentation it should be fired every time that there is a new speaker. It is used by ZOOM UI for changing the yellow frame around the active speaker user.
I according to the documentation, in order to get the current active video user info you should use the following class: MobileRTCMeetingUserInfo.
Check the doc for the video status class MobileRTCVideoStatus: https://marketplacefront.zoom.us/sdk/meeting/ios/interface_mobile_r_t_c_video_status.html
and you will see that is related with the MobileRTCMeetingUserInfo:
https://marketplacefront.zoom.us/sdk/meeting/ios/interface_mobile_r_t_c_meeting_user_info.html
On that class you will find info of the current user.
Hope you can figure out your problem!
Regards!
Gastón Montes.

phone authentication using firebaseUI ios swift

i'm ios fresher. i want to retrieve phone number with country code from the default firebase phone authentication screen.
This is default screen that firebase provides. how can i retrieve country code & phone number on verify buttonClick that is enter by the user and how can i get verify buttonClick event? Is there any library provided function to get buttonClick?
i am stuck here
fileprivate func startLogin() {
self.authUI?.delegate = self
self.authStateListenerHandle = self.auth?.addStateDidChangeListener { (auth, user) in
if user != nil {
print(user)
}else {
do{
try self.auth?.signOut()
print("singning out done")
}catch{
print("Error while signing out!")
}
let phoneProvider = FUIPhoneAuth(authUI: self.authUI!)
self.authUI?.providers = [phoneProvider]
phoneProvider.signIn(withPresenting: self.rootController!, phoneNumber: nil)
}
}
}
this code open phone number login screen for me, bt after providing phone no. when i click to the verify button i have to pass user's phone no. in bellow code to send opt on users mobile, bt i don't know how can i get verify buttonClick event & how can i retrieve phone no. & country code that user input in the phone authentication screen.
PhoneAuthProvider.provider().verifyPhoneNumber(("need to pass phone number with country code here")!, uiDelegate: nil) { (verificationID, error) in
if let error = error {
// self.showMessagePrompt(error.localizedDescription)
return
}
// Sign in using the verificationID and the code sent to the user
// ...
}
Please also explain me right flow of verification.
Why dont you use the Firebase methods to access the phone number once the tocken is verified?
Access the tocken and verify the credibility and after that you can use decodedTocken.phone_number property . Use below to access the phone number
admin.auth().verifyIdToken(idToken).then(function(decodedToken) {
var uid = decodedToken.phone_number;
// ...
}).catch(function(error) {
// Handle error
});
Maybe after you access the phone number you can use the string and break down the country code, just a sugesstion, take a look at the below described answer with how to verify the phone number as well
https://stackoverflow.com/a/45227042/10021979

How to get login user's groups list using XMPP?

I am developing MUC using XMPP framework. I created groups. When the user uninstalls the app and then re-installs it, I want to get all of that user's groups list. I am doing the following to get groups list. Sometimes it returns groups list but sometimes it doesn't.
Configure XMPPMUC
var xmppMuc = XMPPMUC()
xmppMuc = XMPPMUC.init(dispatchQueue: DispatchQueue.main)
xmppMuc.activate(xmppStream)
self.xmppMuc.addDelegate(self, delegateQueue: DispatchQueue.main)
Call discover rooms of XMPPMUC
self.xmppMuc.discoverRooms(forServiceNamed:"conference.app.net")
And the below delegate method of XMPPMUC is also called but sometimes it returns an empty list.
func xmppMUC(_ sender: XMPPMUC, didDiscoverRooms rooms: [Any], forServiceNamed serviceName: String) {
print("MUC : didDiscoverRooms : \(rooms)")
print("MUC : forServiceNamed : \(serviceName)\n")
}
I want the list of all user's groups.

IOS Swift Nested DispatchGroup for Nested Network requests handling arrays

I'm experiencing crashes and I'm not too sure how to handle the situation with nested dispatchgroup inside a dispatchgroup. I know I'm doing something wrong and getting crashes and would like some help with how to handle below situation:
I am using IOS Swift and Firebase and basically grabbing relevant mutual friends by first grabbing a friendList, and then grabbing the friends of each of the friends on my friendList (as those are my mutual friends), if I have not grabbed them earlier (I use a list to track ids of friends Ive already grabbed), I send another network request to fb to grab the number of mutual friends between current user and mutual friend and check if they are relevant enough to be added.
However I have another request after that grabs school friends from firebase and I need to make sure there arent duplicate entries because there are school friends that are also mutual friends. I'm using Dispatch groups like so:
// Iterates through friendList to grab mutual friends
for user in currUser.friendList {
// Grabs user friend list
let userFriendListRef = Database.database().reference().child("friend-list").child(user.userID)
userFriendListRef.observeSingleEvent(of: .value, with: { (snapshot) in
guard snapshot.exists(),
let userFriendList = snapshot.value as? [String: Any] else {
logger.info("No mutual friends grabbed from friend")
return
}
// Mutual friends dispatchGroup
self.mutualFriendsDispatchGroup.enter()
// If exists friends, then see if matches user's interest
self.filterMutualFriendsToMatchUserInterest(using: userFriendList)
})
}
self.mutualFriendsDispatchGroup.notify(queue: .main) {
logger.info("Done mutual friends")
}
// Checks if mutual friend matches interest and then adds it into collectionView
fileprivate func filterMutualFriends(using userFriendList: [String: Any]) {
// Maintains a counter
var searchedMutualFriendCounter = 0
// Iterates through userFriendList
for (userID, _) in userFriendList {
searchedMutualFriendCounter += 1 // Increments counter
// Ensures not repeating a mutual friend
guard usersAddedToHomeScroll[userID] == nil,
searchedUsers[userID] == nil,
!blockedUsers.contains(userID) else {
// Handles mutual friend dispatch group leave condition
if searchedMutualFriendCounter == userFriendList.count {
self.mutualFriendsDispatchGroup.leave()
return
}
continue
}
searchedUsers[userID] = true
grabFriendsDispatchGroup.enter()
// Checks if has enough mutual friends, if yes, grab mutual friend data, else skip
checkIfFriendHasEnoughMutualFriends(userID) { (result) -> Void in
// Makes sure that has enough mutual friends
guard result else {
logger.info("Not enough mutual friends to show in userFriendScroll for \(userID)")
self.grabFriendsDispatchGroup.leave()
// Handles mutual friend dispatch group leave condition
if searchedMutualFriendCounter == userFriendList.count {
self.mutualFriendsDispatchGroup.leave()
}
return
}
logger.info("Mutual friend ID grabbed for \(userID)")
self.grabMutualFriendData(userID, index: searchedMutualFriendCounter, total: userFriendList.count)
}
}
}
fileprivate func getAllFriends() {
// Grabs mutual friends
getMutualFriends()
// Gets school friends
getSchoolFriends()
// Reloads data after grabbing it all
grabFriendsDispatchGroup.notify(queue: .main) {
self.collectionView.reloadData()
}
}
I also call mutualFriendsDispatchGroup.leave() in grabMutualFriendData(...) method.
I apologize for the large amount of code, I was trying to figure out basically how to put in sync lots of network requests nest in a network request to grab mutual friends and before my grab school friends so that I dont get duplicate entries on my collectionView presenting the grabbed users.
Note: The counter thing in filterMutualFriends(...) is a hack I was attempting that would exit out of the outer dispatchgroup once you've iterated through the friendlist of a friend. The outer mutual friends dispatchGroup is the one crashing.
Could not figure out a proper long-term solution to fix the issue, so I had to hack around it and use a bad workaround which just removes duplicate users everytime a new user is grabbed and then reloads the collectionView. However, note that this will and can cause problems in the code.

How To Share Data with Watch OS 2 to display in WKInterfaceTable when working with CoreData

I am using WatchConnectivity to try to send data of type NSManagedObject called arrayOfOjects to the Watch. Each object has a string property called title.
The InterfaceController on the Watch loads and displays and empty table - as intended because the array is empty, then when the user requests the data it is sent using the didReceiveMessage method on the phone.
I am unsure how to add the dictionary array to the objectsArray to display in the WKInterfaceTable.
Does anyone know how I can send the data to the watch to display in the table to make changes and sync them back with the phone ?
Apple Watch:
class ObjectsInterfaceController: WKInterfaceController, WCSessionDelegate {
var session : WCSession!
var objectsArray = [[AnyObject]]()
#IBOutlet var table: WKInterfaceTable!
#IBOutlet var titleLabel: WKInterfaceLabel!
func loadTableData() {
table.setNumberOfRows(self.objectsArray.count, withRowType: "CellRow")
if self.objectsArray.count > 0 {
for (index, name) in self.objectsArray.enumerate() {
let row = self.table.rowControllerAtIndex(index) as! CellRowController
row.objectCellLabel.setText(name.title)
}
}
}
override init() {
super.init()
loadTableData()
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Interface Objects
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
//Check if session is supported and Activate
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
//Swift
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
let value = message["Value"]
dispatch_async(dispatch_get_main_queue()) {
self.objectsArray.removeAll()
self.objectsArray.append(value! as! Array)
self.loadTableData()
}
//send a reply
replyHandler(["Value":"Yes"])
}
}
iPhone
I already fetch all the objects and store in array.
var objectsArray = [Objects]()
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
//send a reply
replyHandler(["Value": [objectsArray]])
}
I need to be able to modify the properties of the objects and save the changes on the iPhone but atm I cannot even send the data and display in the table :( I have been able to send simple string values within a dictionary between devices but not arrays or actual data.
TL/DR:
You can only send basic types (such as strings, integers, doubles) to your watch. This question has more details about sending custom objects.
The other issue:
Even if you archived or serialized the managed objects, it's still not possible to send that particular data from the phone to the watch.
A NSManagedObject is only valid within its own context. In this case, the managed object are registered with a specific NSMangedObjectContext on your iOS app. The managed object is not useful apart from its context, and its managed object context doesn't exist anywhere else but the phone.
NSManagedObject instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through NSManagedObjectID instances.
Since it's not possible pass a managed object from one context (or thread or queue) to another on the same platform, you definitely can't pass a managed object between the phone and its paired watch.
What can you do?
If you had a way to share your Core Data store between the phone and the watch, you could convert the managed object IDs to strings (using URIRepresentation), then pass those strings to the watch, then convert those strings back to object IDs and fetch the corresponding objects. This is explained in detail in this question.
However, app groups are no longer supported on watchOS 2, and it would be very complex to keep two different stores in sync across devices.
A much lighter solution is to pass details about the title, keep track of whatever changes you make on the watch, then send back a dictionary of titles that were inserted, deleted, or otherwise changed.
The phone would then update the managed objects corresponding to those changed titles.
This is similar to how NSFetchedResultsControllerDelegate responds to changes to keep its results in sync.
Does anyone know how I can send the data to the watch to display in the table to make changes and sync them back with the phone?
I gave you a general overview. Anything more detailed would be far too broad to cover in this answer. All I can suggest is that developers either used a third-party framework, or wrote their own implementation.
Some considerations:
Just keep in mind that you don't want to degrade the user's watch experience by transferring large amounts of data back and forth. Keep your watch app lightweight and responsive, as it's ideally only designed to be used for a few seconds.
If you can simplify your watch app design (e.g., only marking a todo list item as completed), you can eliminate much of the "sync" overhead, and delegate the more complex tasks to the iOS app.

Resources