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

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.

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.

Identify incoming callers with CallKit when they are already contacts

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.

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.

How to share data using Watch Connectivity when working with Core Data

In my iOS application I use Core Data to store data and a fetch request to create an array of NSManagedObjects to display in a UITableView.
On the Watch OS I check if WCSession is supported and active a session, then send the iOS application a message from the watchOS extension.
When the iOS application receives the message from the watchOS it should send the array of Objects to the watchOS extension to display the data in the WKInterfaceTable, but I am unsure how to do this. Ultimately what I am trying to achieve is;
How to share the array of Objects with the watchOS extension?
If the user adds/edits/deletes objects in the array on the Watch, how can we update the data on the iPhone ?
Also, the iOS application is embedded within a UITabBarController so does it matter which view controller I communicate with?
Watch OS FavouritesInterfaceController
var session : WCSession!
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()
}
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Interface Objects
//Send Message
sendmessagetoiphone()
}
func sendMessageToIphone() {
if(WCSession.isSupported()){
session.sendMessage(["b":"goodBye"], replyHandler: nil, errorHandler: nil)
}
}
IOS Application : FavouritesViewController
var objects = [Objects]()
func loadData() {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let request = NSFetchRequest(entityName: "Objects")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
do {
try
self.objects = moc.executeFetchRequest(request) as! [Objects]
// success ...
} catch {
// failure
print("Fetch failed")
}
}
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
//handle received message
let value = message["Value"] as? String
dispatch_async(dispatch_get_main_queue()) {
self.messageLabel.text = value
}
//send a reply
replyHandler(["Value":"Hello Watch"])
}
How to share the array of Objects with the Watch OS Extension ?
Since you are using WatchConnectivity framework, send the array of objects from iPhone using sendMessage method and in your FavoritesInterfaceController implement the func session(session: WCSession, didReceiveMessage method in order to get the response or you can get the array in replyhandler to.
If the user adds/edits/deletes objects in the array on the Watch OS
how can we update the data on the iPhone ?
Send the objectId along the with new changes in your sendMessage method from watch to phone, on receiving on phone made the changes in your database save it and send the updated value in your replyHandler so that your watch content will be updated accordingly.
Also the iOS application is embedded within a UITabBarController so
does it matter which view controller I communicate with ?
You desired viewController to which you are communicating OR the one that is responsible for doing changes should be alive. If multiple ViewControllers are listening to WCSessionDelegates then when you send any message from watch all of the live controllers will receive that message. You should include some kind of identifier in your sendMessage dictionary so you can know which operation to perform. Like if you want to delete an object then when watch sends a message the identifier will contain delete so that on receiving you can check the identifier value and perform the delete operation.
You can use the replyHandler in the sendMessage to do this. Make sure you implement the reply handler on both Watch and iOS App to get this.
Basically, if you get it right, your reply handler can ensure what your iOS app does in response for a watch app's message.
Also, speaking of your response (of sending an array of objects) - you should send it as a dictionary and fetch it on the watch.
First of, this is a really good question. For starters I'd recommend that you watch this session from the WWDC 2015: Session 713 - Introducing Watch Connectivity. This can be found here.
Now to your actual question. There is a great tutorial and Github repo that show you how to communicate Core Data between your Apple Watch app and the container app using App Groups, as this enables you to access all shared content, such as Core Data and even NSUSerdefaults.
You can then find the complete tutorial on how to do this under the following link.
Hope that helps, Julian.

Resources