Creating Dialog issue in private Chat with Quickblox - swift

I am trying to implement private 1 to 1 chat with QuickBlox but following the Quickblox docs only shows for group chat in http://quickblox.com/developers/Chat#Create_dialog . When I try sending just single occupants_ids, it gives following error :
{
"errors": [
"Occupants_ids cannot be less than one."
]
}
I am hitting create Dialog API with following body :
{
"type": 3,
"name": "",
"occupant_id": "13822296"
}
Do I need to update some keys in my request body?

Please check: Create new 1-1(private) chat dialog
Code from documentaton work for me:
let chatDialog: QBChatDialog = QBChatDialog(dialogID: nil, type: QBChatDialogType.Private)
chatDialog.occupantIDs = [user.ID]
QBRequest.createDialog(chatDialog, successBlock: {(response: QBResponse?, createdDialog: QBChatDialog?) in completion?(response: response, createdDialog: chatDialog)
print("sucess + \(response)")
}, errorBlock: {(response: QBResponse!) in
print("response + \(response)")
})

QBChatDialog *chatDialog = [[QBChatDialog alloc] initWithDialogID:null type:QBChatDialogTypePrivate];
chatDialog.occupantIDs = #[#(1530190)];
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
} errorBlock:^(QBResponse *response) {
}];
you can use this and you should provide one occupantIds. If it works please let me know.

let user = QBUUser()
user.id = UInt(arrDoctors[sender.tag].QuickBloxId)!
user.fullName = arrDoctors[sender.tag].title.capitalizeFirst
ServicesManager.instance().chatService.createPrivateChatDialog(withOpponent: user) { (response, dialog) in
let chatvc = CHAT_STORYBOARD.instantiateViewController(withIdentifier: "ChatViewController") as! ChatViewController
chatvc.dialog = dialog
self.navigationController?.pushViewController(chatvc, animated: true)
}

Related

Accessing Google API data from within 3 async callbacks and a function in SwiftUI

I know this question is asked a lot, but I can't figure out how to apply any answers to my program. Sorry in advance this async stuff makes absolutely zero sense to me.
Basically, I have a button in SwiftUI that, when pressed, calls a function that makes two API calls to Google Sheets using Alamofire and GoogleSignIn.
Button("Search") {
if fullName != "" {
print(SheetsAPI.nameSearch(name: fullName, user: vm.getUser()) ?? "Error")
}
}
This function should return the values of some cells on success or nil on an error. However, it only ever prints out "Error". Here is the function code.
static func nameSearch<S: StringProtocol>(name: S, advisory: S = "", user: GIDGoogleUser?) -> [String]? {
let name = String(name)
let advisory = String(advisory)
let writeRange = "'App Control'!A2:C2"
let readRange = "'App Control'!A4:V4"
// This function can only ever run when user is logged in, ! should be fine?
let user = user!
let parameters: [String: Any] = [
"range": writeRange,
"values": [
[
name,
nil,
advisory
]
]
]
// What I want to be returned
var data: [String]?
// Google Identity said use this wrapper so that the OAuth tokens refresh
user.authentication.do { authentication, error in
guard error == nil else { return }
guard let authentication = authentication else { return }
// Get the access token to attach it to a REST or gRPC request.
let token = authentication.accessToken
let headers: HTTPHeaders = ["Authorization": "Bearer \(token)"]
AF.request("url", method: .put, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseString { response in
switch response.result {
case .success:
// I assume there is a better way to make two API calls...
AF.request("anotherURL", headers: headers).responseDecodable(of: NameResponseModel.self) { response2 in
switch response2.result {
case .success:
guard let responseData = response2.value else { return }
data = responseData.values[0]
// print(responseData.values[0]) works fine
case .failure:
print(response2.error ?? "Unknown error.")
data = nil
}
}
case .failure:
print(response.error ?? "Unknown error.")
data = nil
}
}
}
// Always returns nil, "Unknown error." never printed
return data
}
The model struct for my second AF request:
struct NameResponseModel: Decodable { let values: [[String]] }
An example API response for the second AF request:
{
"range": "'App Control'!A4:V4",
"majorDimension": "ROWS",
"values": [
[
"Bob Jones",
"A1234",
"Cathy Jones",
"1234 N. Street St. City, State 12345"
]
]
}
I saw stuff about your own callback function as a function parameter (or something along those lines) to handle this, but I was completely lost. I also looked at Swift async/await, but I don't know how that works with callback functions. Xcode had the option to refactor user.authentication.do { authentication, error in to let authentication = try await user.authentication.do(), but it threw a missing parameter error (the closure it previously had).
EDIT: user.authentication.do also returns void--another reason the refactor didn't work (I think).
There is probably a much more elegant way to do all of this so excuse the possibly atrocious way I did it.
Here is the link to Google Identity Wrapper info.
Thanks in advance for your help.
Solved my own problem.
It appears (according to Apple's async/await intro video) that when you have an unsupported callback that you need to run asynchronously, you wrap it in something called a Continuation, which allows you to manually resume the function on the thread, whether throwing or returning.
So using that code allows you to run the Google Identity token refresh with async/await.
private static func auth(_ user: GIDGoogleUser) async throws -> GIDAuthentication? {
typealias AuthContinuation = CheckedContinuation<GIDAuthentication?, Error>
return try await withCheckedThrowingContinuation { (continuation: AuthContinuation) in
user.authentication.do { authentication, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: authentication)
}
}
}
}
static func search(user: GIDGoogleUser) async throws {
// some code
guard let authentication = try await auth(user) else { ... }
// some code
}
I then ran that before using Alamofire's built-in async/await functionality for each request (here's one).
let dataTask = AF.request(...).serializingDecodable(NameResponseModel.self)
let response = try await dataTask.value
return response.values[0]

Post to FB page using iOS SDK fails while Graph API Explorer works

still struggling with this d*** FB SDK :-/
Basically, I've got an app that is supposed to post content to a FB Page. To achieve that, I login using the FB sdk. Then I request authorisations as follow
LoginManager().logIn(permissions: ["pages_manage_posts", "pages_read_engagement", "pages_show_list"], viewController: controller) { result in
print("res \(result)")
switch result {
case .success:
// the pageId is in data>id
Defaults[\.facebookSwitchOn] = true
GraphRequest.init(graphPath: "me/accounts").start { (connexion, result, error) in
guard let result = result as? [String:Any],
let dataArray = result["data"] as? Array<Any>,
let data = dataArray.first as? [String:Any],
let pageId = data["id"] as? String,
let access = data["access_token"] as? String else { return }
print("\(pageId)")
Defaults[\.facebookPageId] = pageId
Defaults[\.facebookPageAccessToken] = access
}
completion(true)
case .failed(let error):
completion(false)
MessageManager.show(.basic(.custom(title: "Oups".local(), message: error.localizedDescription, buttonTitle: nil, configuration: MessageDisplayConfiguration.alert)), in: controller)
default: ()
}
}
I save the pageId and TokenID to be able to perform a POST request as follow
GraphRequest
.init(graphPath: "\(pageId)/photos",
// parameters: ["source" : image, "caption" : text, "access_token" : token, "published" : false],
parameters: ["caption" : contentDescription, "url" : "https://www.cdiscount.com/pdt2/9/2/8/1/700x700/889698377928/rw/figurine-funko-pop-deluxe-game-of-thrones-daen.jpg", "access_token" : token],
httpMethod: .post)
.start { (connexion, result, error) in
completion(Result.success(true))
}
However, I get a weird error telling that publish_actions has been deprecated.
I logged the request using Charles, and here it is https://ibb.co/89wPgKx.
Now here is the debug from the GraphAPI explorer :
curl -i -X POST \ "https://graph.facebook.com/v7.0/104226051340555/photos?caption=test%20message%20from%20Graph%20API%20for%20photo&url=https%3A%2F%2Fwww.cdiscount.com%2Fpdt2%2F9%2F2%2F8%2F1%2F700x700%2F889698377928%2Frw%2Ffigurine-funko-pop-deluxe-game-of-thrones-daen.jpg&access_token=<access token sanitized>"
Basically, it is the same request excepting the fact that parameters in the explorer are URL parameters and they are encapsulated in a json.
I can't understand why the graph explorer request succeeds while the SDK request fails.
I'm totally stuck :-/
Thanks for your help.

Getting a 400 for my AFNetworking get call

I am using AFNetworking for the first time and attempting to make a get call using the FourSquare Places API. When I test with the endpoint on FourSquare's documentation website, my url with params works, but not with AFNetworking code. I keep getting a 400.
func findCoffee(coordinate: CLLocationCoordinate2D) {
let url = "https://api.foursquare.com/v2/venues/explore"
let params = [
"ll": "\(coordinate.latitude),\(coordinate.longitude)",
"intent": intent,
"limit": limit,
"client_id": "\(clientId)",
"client_secret": "\(clientSecret)"
]
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFHTTPRequestSerializer()
manager.get(url, parameters: params, success: { (operation, responseObject) in
}) { (operation, error) in
}
}

Customer does not have linked source with id

I keep getting the following error when viewing stripe test trasactions:
{
"error": {
"type": "invalid_request_error",
"message": "Customer cus_9tW8Cf0Xvm9lRv does not have a linked source with ID tok_19Zj5rAANnhOmz4ex3ri2jtW.",
"param": "source",
"code": "missing"
}
}
my swift code to call the api is as follows:
#IBAction func payButtonWasPressed() {
stripeCard = STPCardParams()
let expirationDate = self.cardExpiryTextField.text!.components(separatedBy: "/")
let expMonth = UInt(expirationDate[0])
let expYear = UInt(expirationDate[1])
stripeCard.number = self.cardNumberTextField.text
stripeCard.cvc = self.cardCVVTextField.text
stripeCard.expMonth = expMonth!
stripeCard.expYear = expYear!
STPAPIClient.shared().createToken(withCard: stripeCard, completion: { (token, error) -> Void in
if error != nil {
self.handleError(error! as NSError)
return
}
self.chargeUsingToken(token!)
})
}
func handleError(_ error: NSError) {
UIAlertView(title: "Please Try Again",
message: error.localizedDescription,
delegate: nil,
cancelButtonTitle: "OK").show()
}
func chargeUsingToken(_ token: STPToken) {
let URL = "https://splitterstripeserver.herokuapp.com/charge"
let params = ["source": token.tokenId,
"stripe_token": token.tokenId,
"amount": total] as [String : Any]
let manager = AFHTTPSessionManager()
manager.post(URL, parameters: params, success: { (operation, responseObject) -> Void in
if let response = responseObject as? [String: String] {
UIAlertView(title: response["status"],
message: response["message"],
delegate: nil,
cancelButtonTitle: "OK").show()
}
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
and I am using the example backend found on the stripe github
On my app I get a pop-up saying Request failed: payment required (402). I have tried a lot of different things but can't seem to get successful response. I cant see what im doing wrong and need a fresh pair of eyes. Any help would be great. Thanks
Once you create the card token with Stripe Checkout or Stripe.js, you send it to your server where you use it to create a customer via the API. That card becomes "saved" with that customer and you can then charge it in the future. If you want to add another card to the same customer you would use the Create Card API to pass the new token and add that card.
When you create a charge, you pass the customer id (cus_XXX) in the customer parameter to charge the default card. If you want to charge a specific card, you would pass the card id (card_YYY) in the source parameter along with the customer.
At the moment, you are trying to pass a customer id and a card token which is not supported. You'll need to save that card on the customer first and then pass the new card id in the source parameter as mentioned above.

Quickblox, chat room does not sends push notification

I created a chat room in Quickblox, when i sends message to chatroom its offline users does not get any push notification.
To send message :
[[QBChat instance] sendMessage:#"Hey! Did you see last Liverpool match?" toRoom:liverpoolFansRoom];
Is there anything that i am doing wrong or its not enabled on Quickbolx server to sends notification on chatroom's offline users.
Thanx
Push messages does not sends automatically. If you know your recipient user's ID you can send push message manually - you should call method provided below:
[QBMessages TSendPushWithText:#"text"
toUsers:(int)userId
delegate:nil];
Swift 3 :
You can send push notifications for all user connected with the dialog using following code:
var payload = [String:String]()
payload["message"] = message.text!
payload["dialog_id"] = self.dialog.id!
do {
let data = try JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
let message = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
var opponentIDs: [String] = []
for userId in self.dialog.occupantIDs! {
// Discard currently logged in user
if userId.uintValue != _user.id {
opponentIDs.append(String(describing: userId))
}
}
let event = QBMEvent()
event.message = message
event.usersIDs = opponentIDs.joined(separator: ",")
event.notificationType = QBMNotificationType.push
event.type = QBMEventType.oneShot
QBRequest.createEvent(event, successBlock: { (response, arrEvents) in
kprint(items: event.name ?? "")
}, errorBlock: { (errRes) in
kprint(items: errRes.error?.description ?? "")
})
} catch {
print(error.localizedDescription)
}