How to listen to Server Side events for Facebook live comments on IOS - swift

I'm using the new SSE endpoints for Live comments on Live videos. my question is how can I achive this on IOS and what library should I use
I tried using the Event Source for IOS https://github.com/inaka/EventSource
But Even though I tried to use OnMessage and addOnEventsource it dosent seem to work.
Here is my code for listening to the live comments
var live_commentURL = URL.init(string:"https://streaming-graph.facebook.com/\(fbLiveStreamId!)/live_comments?access_token=\(accessToken ?? "")&comment_rate=one_per_two_seconds&fields=from{name,id},message")
let queryItems = [NSURLQueryItem(name: "access_token", value: accessToken!), NSURLQueryItem(name: "comment_rate", value: "one_hundred_per_second"),NSURLQueryItem(name: "fields", value: "from{name,id},message")]
let urlComps = NSURLComponents(string: "https://streaming-graph.facebook.com/\(fbLiveStreamId!)/live_comments")!
// var headders:[String:String] = [:]
// headders["access_token"] = accessToken!
// headders["comment_rate"] = "one_hundred_per_second"
// headders["fields"] = "from{name,id},message"
var eventSource = EventSource.init(url:urlComps.url!)
eventSource.connect()
eventSource.onOpen {
print("Successfully Connected to server")
}
eventSource.addEventListener("user-connected") {(id, event, data) in
print("id:", id)
print("event:" , event)
print("data: ", data)
}
eventSource.onMessage { (id, event, data) in
print("id:", id)
print("event:" , event)
print("data: ", data)
}
print(eventSource.events())
eventSource.onComplete { (code, isErro, error) in
print(code)
}
I have tried to send the access tokesn and other fields in headders too but got no luck with it.
I tried 2 methods
method 1
send accesstoken,comment_rateand fields as headders but I dont think that is the right way.
method 2
Since they are all query params I used NSURLComponents.

It was an issue with my auth tokens My bad

Related

Is there any way to map user info with their tweets without too many fetching?

I am now trying to create a simple Twitter client using SwiftUI, and currently at the point where I try to fetch my timeline.
Reading the official Twitter API v2 document, there is a way to fetch timeline, but it only offers tweet ID, text, created_at, and author Id.
so I tried to fetch author info using the author Id in loop, which causes too many fetching data and many of them are redundant because a single user tweet many times.
This way makes my account easily reach the api limit, but I am struggling to find better ways.
I am not an enginner and write codes just for fun, so I am not really familiar with techniques enginners should know... I would appreciate if you give me even a hint.
Here is my code below.
func getStream(client: TwitterAPIClient, id: String) async throws {
let response = await client.v2.timeline.getUserReverseChronological(.init(
id: id,
expansions: TwitterTweetExpansionsV2.all, maxResults: 20,
mediaFields: TwitterMediaFieldsV2.all,
tweetFields: TwitterTweetFieldsV2.all.subtracting([.promotedMetrics, .nonPublicMetrics, .organicMetrics]),
userFields: TwitterUserFieldsV2.all)
)
.responseDecodable(type: TwitterDataResponseV2<
[TwitterTimelineV2],
TwitterTimelineV2.Include,
TwitterTimelineV2.Meta>.self)
if let error = response.error {
print(error)
throw error
} else {
print(response.success!)
}
print(response)
let timeline = try response.map { $0.data }.result.get()
for tweet in timeline {
let response = try await getUserInfo(client: client, id: tweet.authorId).result.get()
print(response)
}
}
Note: TwitterAPIClinet is a client of a third party library.
Instead of fetching user data for each individual tweet, you could first loop the timeline array and store each user once in a dictionary. Then, you can request the user info based on that dictionary.
let timeline = try response.map { $0.data }.result.get()
var usersDictionary = [String : Bool]()
for tweet in timeline {
usersDictionary[tweet.authorId] = true
}
for authorId in usersDictionary.keys {
let response = try await getUserInfo(client: client, id: authorId).result.get()
print(response)
}

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]

Returning parsed JSON data using Alamofire?

Hello new to Swift and Alamofire,
The issue i'm having is when I call this fetchAllUsers() the code will return the empty users array and after it's done executing it will go inside the AF.request closure and execute the rest.
I've done some research and I was wondering is this is caused by Alamofire being an Async function.
Any suggestions?
func fetchAllUsers() -> [User] {
var users = [User]()
let allUsersUrl = baseUrl + "users/"
if let url = URL(string: allUsersUrl) {
AF.request(url).response { response in
if let data = response.data {
users = self.parse(json: data)
}
}
}
return users
}
You need to handle the asynchrony in some way. This this means passing a completion handler for the types you need. Other times it means you wrap it in other async structures, like promises or a publisher (which Alamofire also provides).
In you case, I'd suggest making your User type Decodable and allow Alamofire to do the decoding for you.
func fetchAllUsers(completionHandler: #escaping ([User]) -> Void) {
let allUsersUrl = baseUrl + "users/"
if let url = URL(string: allUsersUrl) {
AF.request(url).responseDecodable(of: [User].self) { response in
if let users = response.value {
completionHandler(users)
}
}
}
}
However, I would suggest returning the full Result from the response rather than just the [User] value, otherwise you'll miss any errors that occur.

CNContactVCardSerialization.data(with:) always returns nil result

I am trying to retrieve all contacts and save them in VCard form (swift 4, XCode 9.0). But CNContactVCardSerialization.data(with:) always returns nil. Here is my code:
var contacts = [CNContact]()
let request = CNContactFetchRequest(keysToFetch:[CNContact.descriptorForAllComparatorKeys()])
do {
try contactsStore.enumerateContacts(with: request, usingBlock:
{ (contact:CNContact, result:UnsafeMutablePointer<ObjCBool>) in
self.contacts.append(contact)
})
}
catch {
}
// at this point all contacts are in the "contacts" array.
var data = Data()
do {
try data = CNContactVCardSerialization.data(with: contacts)
}
catch {
print("some error in contacts:" + String(describing: error));
}
print(">>>data:" + String(data.count))
Output:
2017-11-02 XXX [5224:449081]
Exception writing contacts to vCard (data): A property was not
requested when contact was fetched.
2017-11-02 XXX [5224:449362] XPC
connection interrupted
some error in contacts:nilError
>>>data:0
I red the question below but it does not help.
How to use method dataWithContacts in CNContactVCardSerialization?
I added "Privacy - Contacts Usage Description" into info.plist
Maybe you need to provide some specific keys to fetch?
UPD: Yep, if you want to fetch requests and serialize them, you have to set keys to fetch:
keysToFetch:#[[CNContactVCardSerialization descriptorForRequiredKeys]]
Change
let request = CNContactFetchRequest(keysToFetch:[CNContact.descriptorForAllComparatorKeys()])
To
let request = CNContactFetchRequest(keysToFetch:[CNContactVCardSerialization.descriptorForRequiredKeys()])

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)
}