When i terminate app MUC group chat members are getting removed - ios

When i terminate app MUC group chat members are getting removed, i have to join them again while coming back to app from bookmarks? We do not want to rejoin again and again. Can someone please suggest way how to avoid rejoining.
In Android smack there is provision for auto-rejoin.
Even from Openfire back end we have managed code to do not remove.
So Android is working fine, iOS is removing users.
Please do suggest.

Instead of rejoining the room every time, do set the presence of the group when the user relaunches the app.
Set presence with below code function iterate through all your groups name and set presence:
for group in chatListModel ?? []{
if(group.opponent_type == "2"){
print("Group Name: \(group.opponent_uuid ?? "")")
XMPPGlobal.sharedIntanceXMPP.xmppController.updatePresence(roomJID: XMPPJID(string: "\(group.opponent_uuid ?? "")#\(groupServerName)"))
}
}
Define below function in your XMPPController class:
func updatePresence(roomJID : XMPPJID?) {
let presence = XMPPPresence(type: "presence")
presence.addAttribute(withName: "from", stringValue: self.xmppStream.myJID?.user ?? "")
presence.addAttribute(withName: "to", stringValue: "\(roomJID?.full ?? "")/\(self.xmppStream.myJID?.user ?? "")")
let element = DDXMLElement.init(name: "x", xmlns: XMPPMUCNamespace)
presence.addChild(element)
self.xmppStream.send(presence)
}
Hope it will works for you.

In spite of the fact that there is the answer.
I would like to elaborate a little bit :)
In case of MUC rooms: there is an affiliation, i.e. long-lasting role (admin, owner, etc) and "subscription" role (visitor, member etc).
What you are asking is per se described in XMPP MUC. 7.1 Order of Events
You send your presence to join the room, and receive presence from other other participants, you can also get some cached messages if your XMPP backend was configured properly.
For instance for ejabberd (process-one): mod_muc: history_size settings defines in-memory cache.
You might don't want to get set of room events, as described in 7.1 above.
There might be some server's extensions. In case of ejabberd there is MUCPubSub you send your subscription (not presence) and get the following messages, plus you can get list of members or track its changes, and etc:
<subscribe xmlns='urn:xmpp:mucsub:0'
nick='mynick'
password='roompassword'>
<event node='urn:xmpp:mucsub:nodes:messages' />

Related

AWS AppSync multiple subscriptions in same view controller not working - iOS Swift

Just FYI I posted this question originally in the AWS AppSync forum (in case in the future AWS answers it).
I have been trying to make a simple Posts app like the one in the docs but I have found no documentation or guides that handle multiple subscriptions in one view controller.
Three mutations: onCreatePost, onUpdatePost, onDeletePost
(and of course three subscriptions to those mutations)
In Xcode, I have three functions called during viewDidLoad(): subscribeToNewPosts(), subscribeToUpdatedPosts(), subscribeToDeletedPosts()
Each subscription function works and creates a subscription with the correct functionality and updates the table view accordingly if used alone. But, if called one after the other, only the last subscription will actually receive data and update the table view. I put a breakpoint to check out topicSubscribersDictionary in AppSyncMQTTClient.swift after subscribing to all three mutations
func startNewSubscription(subscriptionInfo: AWSSubscriptionInfo) {
var topicQueue = [String]()
let mqttClient = MQTTClient<AnyObject, AnyObject>()
mqttClient.clientDelegate = self
for topic in subscriptionInfo.topics {
if topicSubscribersDictionary[topic] != nil {
// if the client wants subscriptions and is allowed we add it to list of subscribe
topicQueue.append(topic)
}
}
mqttClients.append(mqttClient)
mqttClientsWithTopics[mqttClient] = topicQueue
mqttClient.connect(withClientId: subscriptionInfo.clientId, toHost: subscriptionInfo.url, statusCallback: nil)
}
and all three subscriptions are in fact in the dictionary...
Do I need multiple instances of appSyncClient, one for each subscription? Is it a problem with the schema design?
schema.graphql
schema.json
mutations.graphql
queries.graphql
subscriptions.graphql
Example use case: simple chat app. New conversation started = OnCreatePostSubscription; new incoming message in that conversation = OnUpdatePostSubscription
Are you using API Key for authorization in AppSync? If you are using API Key only one subscription is supported by the SDK at this point. Could you switch to IAM (Cognito Identity) or Cognito UserPools based auth and see if multiple subscriptions work for you?
I managed to have several subscriptions working with API Key by replacing the call startSubscriptions to startNewSubscription inside AWSAppSyncSubscriptionWatcher
if let subscriptionInfo = subscriptionResult.subscrptionInfo {
self.subscriptionTopic = subscriptionResult.newTopics
self.client?.addWatcher(watcher: self, topics: subscriptionResult.newTopics!, identifier: self.uniqueIdentifier)
//self.client?.startSubscriptions(subscriptionInfo: subscriptionInfo)
subscriptionInfo.forEach { self.client?.startNewSubscription(subscriptionInfo: $0) }
}
Couldn't find any side effect with this approach yet, apart from requiring to fork the iOS SKD

How can a Slack bot detect a direct message vs a message in a channel?

TL;DR: Via the Slack APIs, how can I differentiate between a message in a channel vs a direct message?
I have a working Slack bot using the RTM API, let's call it Edi. And it works great as long as all commands start with "#edi"; e.g. "#edi help". It currently responses to any channel it's a member of and direct messages. However, I'd like to update the bot so that when it's a direct message, there won't be a need to start a command with "#edi"; e.g. "#edi help" in a channel, but "help" in a direct message. I don't see anything specific to differentiate between the two, but I did try using the channel.info endpoint and counting the number of people in "members"; however, this method only works on public channel. For private channels and direct messages, the endpoint returns an "channel_not_found" error.
Thanks in advance.
I talked to James at Slack and he gave me a simply way to determine if a message is a DM or not; if a channel ID begins with a:
C, it's a public channel
D, it's a DM with the user
G, it's either a private channel or multi-person DM
However, these values aren't set in stone and could change at some point, or be added to.
So if that syntax goes away, another way to detect a DM to use both channels.info and groups.info. If they both return “false” for the “ok” field, then you know it’s a DM.
Note:
channels.info is for public channels only
groups.info is for private channels and multi-person DMs only
Bonus info:
Once you detect a that a message is a DM, use either the user ID or channel ID and search for it in the results of im.list; if you find it, then you’ll know it’s a DM to the bot.
“id” from im.list is the channel ID
“user” from im.list is the user ID from the person DM’ing with the bot
You don’t pass in the bot’s user ID, because it’s extracted from the token
FYI as of July 2017, for "message.im" events (via your app's Event Subscriptions), the event payload seems to now return additional fields to detect if the message is coming from your own bot (pasted in here from my logs):
INFO[0012] got Slack message: (bot.SlackMessage) {
SlackEvent: (bot.SlackEvent) {
Type: (string) (len=7) "message",
EventTs: (string) (len=17) "1501076832.063834",
User: (string) ""
},
SubType: (string) (len=11) "bot_message",
Channel: (string) (len=9) "D6CJWD132",
Text: (string) (len=20) "this is my bot reply",
Username: (string) (len=15) "Myapp Local",
BotID: (string) (len=9) "B6DAZKTGG",
Ts: (string) (len=17) "1501076832.063834"
}
Slack have added Conversations API some time ago. You should use it to differentiate between PM/channel instead of relying on prefix.
From Conversations API documentation:
Each channel has a unique-to-the-team ID that begins with a single letter prefix, either C, G, or D. When a channel is shared across teams (see Developing for Shared Channels), the prefix of the channel ID may be changed, e.g. a private channel with ID G0987654321 may become ID C0987654321.
This is one reason you should use the conversations methods instead of the previous API methods! You cannot rely on a private shared channel's unique ID remaining constant during its entire lifetime.
Get conversation info using conversations.info method and check is_im flag. is_im == true means that the conversation is a direct message between two distinguished individuals or a user and a bot.
The info function is also available for private channels with the Slack API method groups.info. This works also for direct message channels with multiple participants, since they are a special form of private channels.
You can use groups.list to get the IDs of all private channels incl. direct message channels with multiple participants.
Note that groups.list will only return private channels, that the user or bot that the access token belongs to has been invited to.

iOS: How to detect if a user is subscribed to an auto-renewable subscription

Hopefully the title is self-explanatory. I'm trying to do something like this:
checkIfUserIsSubscribedToProduct(productID, transactionID: "some-unique-transaction-string", completion: { error, status in
if error == nil {
if status == .Subscribed {
// do something fun
}
}
}
does anything like the hypothetical code I've provided exist? I feel like I'm taking crazy pills
Edit
In similar questions I keep seeing a generic answer of "oh you gotta validate the receipt" but no explanation on how, or even what a receipt is. Could someone provide me with how to "validate the receipt"? I tried this tutorial but didn't seem to work.
Edit - For Bounty
Please address the following situation: A user subscribes to my auto-renewable subscription and gets more digital content because of it - cool, implemented. But how do I check whether that subscription is still valid (i.e. they did not cancel their subscription) each time they open the app? What is the simplest solution to check this? Is there something like the hypothetical code I provided in my question? Please walk me through this and provide any further details on the subject that may be helpful.
I know everyone was very concerned about me and how I was doing on this - fear not, solved my problem. Main problem was that I tried Apple's example code from the documentation, but it wasn't working so I gave up on it. Then I came back to it and implemented it with Alamofire and it works great. Here's the code solution:
Swift 3:
let receiptURL = Bundle.main.appStoreReceiptURL
let receipt = NSData(contentsOf: receiptURL!)
let requestContents: [String: Any] = [
"receipt-data": receipt!.base64EncodedString(options: []),
"password": "your iTunes Connect shared secret"
]
let appleServer = receiptURL?.lastPathComponent == "sandboxReceipt" ? "sandbox" : "buy"
let stringURL = "https://\(appleServer).itunes.apple.com/verifyReceipt"
print("Loading user receipt: \(stringURL)...")
Alamofire.request(stringURL, method: .post, parameters: requestContents, encoding: JSONEncoding.default)
.responseJSON { response in
if let value = response.result.value as? NSDictionary {
print(value)
} else {
print("Receiving receipt from App Store failed: \(response.result)")
}
}
As some comments pointed out there's a couple flaws with these answers.
Calling /verifyReceipt from the client isn't secure.
Comparing expiration dates against the device clock can be spoofed by changing the time (always a fun hack to try after cancelling a free trial :) )
There are some other tutorials of how to set up a server to handle the receipt verification, but this is only part of the problem. Making a network request to unpack and validate a receipt on every app launch can lead to issues, so there should be some caching too to keep things running smoothly.
The RevenueCat SDK provides a good out-of-the box solution for this.
A couple reasons why I like this approach:
Validates receipt server side (without requiring me to set up a server)
Checks for an "active" subscription with a server timestamp so can't be spoofed by changing the device clock
Caches the result so it's super fast and works offline
There's some more details in this question: https://stackoverflow.com/a/55404121/3166209
What it works down to is a simple function that you can call as often as needed and will return synchronously in most cases (since it's cached).
subscriptionStatus { (subscribed) in
if subscribed {
// Show that great pro content
}
}
What are you trying to achieve in particular? Do you want to check for a specific Apple ID?
I highly doubt that this is possible through the SDK. Referring to Is it possible to get the user's apple ID through the SDK? you can see that you can't even ask for the ID directly but rather services attached to it.
What would work is caching all transactions on your own server and search its database locally but that would require the app to ask for the user's Apple ID so the app could update the subscription state whenever it launches as it can check for IAP of the ID associated with the device.
However, the user could just type whatever he wanted - and it's unlikely to get this through Apple's app review process.
I am using MKSoreKit https://github.com/MugunthKumar/MKStoreKit for auto-renew subscriptions.but it is in objective c you can check the library code for solution.I am using it in my code and it is working fine.
using below method you can easily check subscription status..
if([MKStoreManager isProductPurchased:productIdentifier]) {
//unlock it
}
It gets the apple id from device and I think that is user specific

How to create contacts list like whats app did

I'm working on an app that creates an user profile. I need to be able to see who from my contacts has the app installed so that i am able to grant them access to my profile and see their profiles.
How do i get the full contact list, determine my phone number to create my account with it, and check how many people from the contacts have the app installed? (similar to how WhatsApp does it)
I know it's possible to get the contact list by using Apple API's, but do i have to send all the information to the server to check if they have the app installed?
Thank you.
I think you are managing one local database to store users locally.
First, You must have one contact mapping table on server that determines which number is allocate to which user (Each user must have unique number per account).
You have to pass array of phone numbers to your server request and fire query to get only those users whose phone numbers are in array.
You get one users objects array and that array you have to save in local database. (You can store all contacts in local database and update record (for appId, contactId, server name etc) is user is application user).
Finally these users who has appId are your application users.
Hope this concept might help you.
If you are using webservices your backend will can give the contacts which are using your application.
For getting the app contacts you have to use user phone number using phone number you can get the app user through webservices
to get all contact from Phonebook you can use below method in swift
first import Contacts
func FetchContact()
{
if #available(iOS 9.0, *) {
let contactStore = CNContactStore()
do {
let request:CNContactFetchRequest
request = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactMiddleNameKey, CNContactEmailAddressesKey,CNContactPhoneNumbersKey])
request.sortOrder = CNContactSortOrder.GivenName
try contactStore.enumerateContactsWithFetchRequest(request) {
(contact, cursor) -> Void in
self.dataArray?.addObject(contact)
}
}
catch{
print("Handle the error please")
}
//self.collectionView?.reloadData()
}
}

Struggling to Understand CKSubscriptions in CloudKit

I've been reading about and trying to use the CKSubscription feature for weeks, but I can't find info about some general questions. I've read Apple docs, online tutorials, books I bought, and questions here on SO, but I still don't understand the fundamentals I think. Any help is very much appreciated.
Here are questions I cannot find answers to:
1). What is the purpose of the subscriptionID? The convenience init does not include it, so why is it needed in the designated init? If you use it, is it the same for all users of the app?
2). I saw someone mention here that you can unregister a subscriptionID. Why or how would you do this?
3). Can subscriptions be setup in both public or the user's private database?
4). If I have a query based subscription that is the same for all users, will there only ever be 1 subscription listed in the database?
For instance, I'm having trouble getting notifications to work with my
specific use case. It's not a problem in my setup, as I can get a True predicate to work and the notification comes. So I must not understand the fundamentals of how subscriptions work still.
I'm trying to setup a subscription that fires whenever a new record is created when a different user makes a comment on a post. This new record will then contain a reference to the user who created the post. The only subscription I see in the database for both users is - Notifications.user (equals reference). So, I'm assuming I'll only ever see this one subscription.(?) But how does the server keep track of every user's recordID or know when to send it to a specific device?
The problem is I can't get the notification to work. I manually add a record in the dashboard, and I put the other user's recordID as the CKReference. While I'm adding the record, I have the app running in the background on a device under the user's account whom I added as the CKReference in the field. I'd expect the query to trigger and send a push notification since someone commented on this user's post.
Here's my code to setup the subscription:
func subscribe(userID: CKRecordID) {
let options = CKSubscriptionOptions.FiresOnRecordCreation
let userRef = CKReference(recordID: userID, action: .DeleteSelf)
let predicate = NSPredicate(format: "userRef == %#", userRef)
let predicateTwo = NSPredicate(format: "read = %#", "")
let compoundPred = NSCompoundPredicate(type: .AndPredicateType, subpredicates: [predicate, predicateTwo])
let subscription = CKSubscription(recordType: "Notifications", predicate: compoundPred, subscriptionID: subscriptionID,
options: options)
subscription.notificationInfo = CKNotificationInfo()
subscription.notificationInfo.desiredKeys = ["userPost"]
subscription.notificationInfo.alertBody = "Someone replied to your Pod post"
publicDB.saveSubscription(subscription, completionHandler: {
subscription, error in
if (error != nil) {
println("error subscribing: \(error)")
} else {
println("subscribed!")
}
})
}
Let me try and answer:
SubscriptionId allows you to identify later a subscription for example to delete it using the CKDatabase method deleteSubscriptionWithID
For how see answer 1. As to why, well maybe you do not want to get notifications on this subscription any more. This depends on what you are trying to achieve with your app.
Yes
Yes, if you register only one subscription it should work for all users of your app
Regarding your issues, please note that a user recordIDs are special, so you may have issues specifically related to that due to the privacy issues around them. I would suggest to try a simple case that does not involve users and see if subscriptions are working for you. Then think again about how you use user record IDs

Resources