How to subscribe channel with user presence in pubnub with swift? - ios

I am using pubnub SDk for socket stuffs. pubnub provides a feature to know the online/offline users with it's method hereNow. But it's not getting called when the user gets online or goes offline as this should be involked like socket listener. If anybody has some idea about this please help me out.
func registerForConnectRoom(roomId: String) {
pubnub.subscribe(to: ["ch:callroom:" + roomId], withPresence: true)
let listener = SubscriptionListener()
listener.didReceiveSubscription = { event in
switch event {
case let .messageReceived(message):
print("heuuu \(message)")
case let .connectionStatusChanged(status):
print("Status Received: \(status)")
case let .presenceChanged(presence):
print("Presence Received: \(presence)")
case let .subscribeError(error):
print("Subscription Error \(error)")
default:
break
}
}
}
func hereNow(roomId: String) {
pubnub.hereNow(on: ["ch:callroom:" + roomId], includeState: true) { result in
switch result {
case let .success(presenceByChannel):
print("Total channels \(presenceByChannel.totalChannels)")
print("Total occupancy across all channels \(presenceByChannel.totalOccupancy)")
if let myChannelPresence = presenceByChannel["ch:callroom:" + roomId] {
print("The occupancy for `my_channel` is \(myChannelPresence.occupancy)")
print("The list of occupants for `my_channel` are \(myChannelPresence.occupants)")
}
case let .failure(error):
print("Failed hereNow Response: \(error.localizedDescription)")
}
}
}
calling these methods in viewDidLoad

Related

How to make a message filter with subAction for iOS

I am making a sms message filter for iOS, I want to classify some message with subAction
I returned subAction, But there is no classification in my iPhone(iOS 16), and my app will classify them to promotion or transaction
this is my code:
import IdentityLookup
final class MessageFilterExtension: ILMessageFilterExtension {}
extension MessageFilterExtension: ILMessageFilterQueryHandling, ILMessageFilterCapabilitiesQueryHandling {
func handle(_ capabilitiesQueryRequest: ILMessageFilterCapabilitiesQueryRequest, context: ILMessageFilterExtensionContext, completion: #escaping (ILMessageFilterCapabilitiesQueryResponse) -> Void) {
let response = ILMessageFilterCapabilitiesQueryResponse()
// TODO: Update subActions from ILMessageFilterSubAction enum
completion(response)
}
func handle(_ queryRequest: ILMessageFilterQueryRequest, context: ILMessageFilterExtensionContext, completion: #escaping (ILMessageFilterQueryResponse) -> Void) {
let (offlineAction, offlineSubAction) = self.offlineAction(for: queryRequest)
print("====== STARTED ======")
switch offlineAction {
case .allow, .junk, .promotion, .transaction:
// Based on offline data, we know this message should either be Allowed, Filtered as Junk, Promotional or Transactional. Send response immediately.
let response = ILMessageFilterQueryResponse()
response.action = offlineAction
response.subAction = offlineSubAction
completion(response)
case .none:
// Based on offline data, we do not know whether this message should be Allowed or Filtered. Defer to network.
// Note: Deferring requests to network requires the extension target's Info.plist to contain a key with a URL to use. See documentation for details.
context.deferQueryRequestToNetwork() { (networkResponse, error) in
let response = ILMessageFilterQueryResponse()
response.action = .none
response.subAction = .none
if let networkResponse = networkResponse {
// If we received a network response, parse it to determine an action to return in our response.
(response.action, response.subAction) = self.networkAction(for: networkResponse)
} else {
NSLog("Error deferring query request to network: \(String(describing: error))")
}
completion(response)
}
#unknown default:
break
}
}
private func offlineAction(for queryRequest: ILMessageFilterQueryRequest) -> (ILMessageFilterAction, ILMessageFilterSubAction) {
// TODO: Replace with logic to perform offline check whether to filter first (if possible).
NSLog("recived msg: " + queryRequest.messageBody! + " sender: " + queryRequest.sender!)
// reload setting
SettingManager.getInstance().load()
let whitelistResult = whitelistHandler(sender: queryRequest.sender!)
if whitelistResult == .allow {
return (.allow, .none)
}
let callingCodeResult = callingCodeHandler(sender: queryRequest.sender!)
if callingCodeResult != .none {
return (callingCodeResult, .none)
}
if bombingHandler(msg: queryRequest.messageBody!) {
return (.junk, .none)
}
let keywordResult = keywordHandler(msg: queryRequest.messageBody!)
if keywordResult != .none {
return (keywordResult, .none)
}
let classificationResult = classifyHandler(msg: queryRequest.messageBody!)
if classificationResult.0 != .none {
return (classificationResult.0, classificationResult.1)
}
return (.none, .none)
}
func classifyHandler(msg: String) -> (ILMessageFilterAction, ILMessageFilterSubAction) {
NSLog("checking msg's classification")
let classificationFilters = DataManager.getInstance().getClassificationData()
for filter in classificationFilters {
NSLog("checking keyword: \(filter.keyword)")
if (msg.contains(filter.keyword)) {
NSLog("msg is in classification list, marked to \(filter.type)")
return (filter.type.getAction(), filter.type.getSubAction())
}
}
NSLog("msg is not in classification")
return (.none, .none)
}
func whitelistHandler(sender: String) -> ILMessageFilterAction {
...
return .none
}
func callingCodeHandler(sender: String) -> ILMessageFilterAction {
...
return .none
}
// false: not need to filter
// true: need filter
func bombingHandler(msg: String) -> Bool {
...
return false
}
func keywordHandler(msg: String) -> ILMessageFilterAction {
...
return .none
}
}
I tried to set response's promotionalSubActions and transactionSubActions in first handle function, But it does not works

PHPhoto localIdentifier to cloudIdentifier conversion code improvements?

The two conversion methods below for mapping between the PHPhoto localIdentifier to the corresponding cloudIdentifier work but it feels too heavy. Do you have suggestions on how to rewrite to a more elegant (easier to read) form?
The sample code in the Apple documentation found in PHCloudIdentifier https://developer.apple.com/documentation/photokit/phcloudidentifier/ does not compile in xCode 13.2.1.
It was difficult to rewrite the sample code because I made the mistake of interpreting the Result type as a tuple. Result type is really an enum.
func localId2CloudId(localIdentifiers: [String]) -> [String] {
var mappedIdentifiers = [String]()
let library = PHPhotoLibrary.shared()
let iCloudIDs = library.cloudIdentifierMappings(forLocalIdentifiers: localIdentifiers)
for aCloudID in iCloudIDs {
//'Dictionary<String, Result<PHCloudIdentifier, Error>>.Element' (aka '(key: String, value: Result<PHCloudIdentifier, Error>)')
let cloudResult: Result = aCloudID.value
// Result is an enum .. not a tuple
switch cloudResult {
case .success(let success):
let newValue = success.stringValue
mappedIdentifiers.append(newValue)
case .failure(let failure):
// do error notify to user
let iCloudError = savePhotoError.otherSaveError // need to notify user
}
}
return mappedIdentifiers
}
func cloudId2LocalId(assetCloudIdentifiers: [PHCloudIdentifier]) -> [String] {
// patterned error handling per documentation
var localIDs = [String]()
let localIdentifiers: [PHCloudIdentifier: Result<String, Error>]
= PHPhotoLibrary
.shared()
.localIdentifierMappings(
for: assetCloudIdentifiers)
for cloudIdentifier in assetCloudIdentifiers {
guard let identifierMapping = localIdentifiers[cloudIdentifier] else {
print("Failed to find a mapping for \(cloudIdentifier).")
continue
}
switch identifierMapping {
case .success(let success):
localIDs.append(success)
case .failure(let failure) :
let thisError = failure as? PHPhotosError
switch thisError?.code {
case .identifierNotFound:
// Skip the missing or deleted assets.
print("Failed to find the local identifier for \(cloudIdentifier). \(String(describing: thisError?.localizedDescription)))")
case .multipleIdentifiersFound:
// Prompt the user to resolve the cloud identifier that matched multiple assets.
default:
print("Encountered an unexpected error looking up the local identifier for \(cloudIdentifier). \(String(describing: thisError?.localizedDescription))")
}
}
}
return localIDs
}

AWS Cognito Import throw userNotFound

I'm trying to migrate a batch of users into Cognito via a CSV (i.e. one-time import). This part works successfully and I can see the users in the User Pool. As expected, I see the user with a status of "Enabled / RESET_REQUIRED". So far so good, right?
When I try to sign-in to the user in my mobile app, it throws an error of "userNotFound" instead of the "passwordResetRequired" which would trigger the proper flow.
Has anyone seen this? Or know how to solve this problem?
Just for grins, here is my code:
private func signInWithAWS(email: String, password: String) -> Observable<AWSSignInResult> {
Observable<AWSSignInResult>.create { observer in
AWSMobileClient.default().signIn(
username: email.lowercased(),
password: password,
completionHandler: { signInResult, error in
if let signInResult = signInResult {
switch signInResult.signInState {
case .signedIn:
observer.onNext(.success)
observer.onCompleted()
default:
observer.onCompleted()
}
} else if let error = error as? AWSMobileClientError {
if case AWSMobileClientError.userNotConfirmed = error {
observer.onNext(.unconfirmed)
observer.onCompleted()
return
} else if case AWSMobileClientError.passwordResetRequired = error {
observer.onNext(.resetPasswordRequired)
observer.onCompleted()
}
let message = getCognitoErrorMessage(error: error)
observer.onError(AuthServiceError.signIn(message))
}
}
)
return Disposables.create()
}
}
It fails the "userNotConfirmed" and "passwordResetRequired" checks into the generic handler because the actual error is "userNotFound".

Is it possible to connect to WiFi automatically by capturing QR-Code?

I'm newbie in iOS development(SwiftUI).
Recently, I have encountered a problem about connecting to WiFi by capturing QR-Code.
I wonder that if is there any possible solution to connect to WiFi directly by using QR-Code.
After I did a lot of research, I still cannot find any references about this issue.
There are my keywords: SwiftUI, Swift, QR-Code, Wi-Fi, iOS.
What do I have now:
ContentView
struct ContentView: View {
#State private var isShowingScanner = false
#State private var resultOfScanning: String = "Result will be shown here..."
var body: some View {
VStack {
Button(action: { self.isShowingScanner = true }, label: { Text("Scan Button") })
.sheet(isPresented: self.$isShowingScanner) {
CodeScannerView(codeTypes: [.qr], simulatedData: "www.opgg.com", completion: self.handleScan)
}
Text("\(resultOfScanning)")
}
}
func handleScan(result: Result<String, CodeScannerView.ScanError>) {
self.isShowingScanner = false
switch result {
case .success(let code):
let codeStr = code as! String
self.resultOfScanning = codeStr
case .failure(let error):
print("Scanning failed")
}
}
}
CodeScannerView (Reference: An article by Paul Hudson)
What do I wanna build:
I want to use this App, connecting to the specific WiFi automatically by capturing QR-Code(It contains SSID & Password of WiFi).
What is my problem:
I cannot find a way to connect to the specific WiFi automatically by using QR-Code. It seems that no one have talked about this issue.
May someone know that how to solve the problems like above-mentioned?
Thanks for comments and answers.
We have four steps to deal with this issue.
1.Capture the QR-Code which contains the specific WiFi hotspot information(SSID, Password, Encryption Type).
2.Convert JSON data of QR-Code to Dictionary.
3.Get the SSID, Password and Encryption type value from before-mentioned Dictionary.
4.Use NEHotspotConfiguration of Apple API to set our SSID, Password and Encryption type and connect to the specific WiFi Hotspot.
p.s. Your app needs the signing certificate to active some function of your project and Apple API.
Let's see what have I done here:
func handleScan(result: Result<String, CodeScannerView.ScanError>) {
switch result {
case .success(let code):
let data_code = code.data(using: .utf8)
do {
let dict_code = try JSONSerialization.jsonObject(with: data_code!, options: .allowFragments) as! [String : Any]
let wifi_ssid = dict_code["S"] as! String
let wifi_pwd = dict_code["P"] as! String
let wifi_type = dict_code["T"] as! String
let configuration = NEHotspotConfiguration.init(ssid: wifi_ssid, passphrase: wifi_pwd, isWEP: self.checkWifiType(type: wifi_type))
configuration.joinOnce = true
NEHotspotConfigurationManager.shared.apply(configuration) {
(error) in
if error != nil {
if let errorStr = error?.localizedDescription {
print("Error Information:\(errorStr)")
}
if (error?.localizedDescription == "already associated.") {
print("Connected!")
} else {
print("No Connected!")
}
} else {
print("Connected!")
}
}
print("Dict_Code:\(dict_code)")
} catch (let error) {
print("JSONSerial... Convert Error:\(error.localizedDescription)")
}
case .failure(let error):
self.connectionStatus = "Scanning failed!"
}
}
After doing this, I finally can scan my own QR-Code and connect to the specific WiFi Hotspot.

Create serial queue for network calls generated by push notifications

I am receiving up to four push notifications for each event I am subscribed to. I have gone through everything related to my CloudKit subscriptions and notification registry and I am convinced this is an Apple problem. I have instead turned my attention toward correctly processing the notifications no matter how many I receive. Here is a simplified version of what I am doing:
func recievePrivatePush(_ pushInfo: [String:NSObject], completion: #escaping ()->Void) {
let notification = CKNotification(fromRemoteNotificationDictionary: pushInfo)
let alertBody = notification.alertBody
if let queryNotification = notification as? CKQueryNotification {
let recordID = queryNotification.recordID
guard let body = queryNotification.alertBody else {
return
}
if recordID != nil {
switch body {
case "Notification Type":
let id = queryNotification.recordID
switch queryNotification.queryNotificationReason {
case .recordCreated:
DataCoordinatorInterface.sharedInstance.fetchDataItem(id!.recordName, completion: {
//
})
break
default:
break
}
}
}
}
}
The fetching code looks something like this:
func fetchDataItem(_ id: String, completion: #escaping ()-> Void) {
if entityExistsInCoreData(id) {return}
let db = CKContainer.default().privateCloudDatabase
let recordID = CKRecordID(recordName: id)
db.fetch(withRecordID: recordID) { (record, error) in
if let topic = record {
//Here I create and save the object to core data.
}
completion()
}
}
All of my code works, the problem I am having is that when I receive multiple notifications, multiple fetch requests are started before the first core data entity is created, resulting in redundant core data objects.
What I would like to do is find a way to add the fetch requests to a serial queue so they are processed one at a time. I can put my request calls in a serial queue, but the callbacks always run asynchronously, so multiple fetch requests are still make before the first data object is persisted.
I have tried using semaphores and dispatch groups with a pattern that looks like this:
let semaphore = DispatchSemaphore(value: 1)
func recievePrivatePush(_ pushInfo: [String:NSObject], completion: #escaping ()->Void) {
_ = semaphore.wait(timeout: .distantFuture)
let notification = CKNotification(fromRemoteNotificationDictionary: pushInfo)
let alertBody = notification.alertBody
if let queryNotification = notification as? CKQueryNotification {
let recordID = queryNotification.recordID
guard let body = queryNotification.alertBody else {
return
}
if recordID != nil {
switch body {
case "Notification Type":
let id = queryNotification.recordID
switch queryNotification.queryNotificationReason {
case .recordCreated:
DataCoordinatorInterface.sharedInstance.fetchDataItem(id!.recordName, completion: {
semaphore.signal()
})
break
default:
break
}
}
}
}
}
Once the above function is called for the second time, and semaphore.wait is called, the execution of the first network request pauses, resulting in a frozen app.
Again, what I would like to accomplish it adding the asynchronous network requests to a queue so that they are made only one at a time i.e. the first network call is completed before the second request is started.
Carl,
Perhaps you'll find your solutions with dispatch groups, a few key expressions to look into.
let group = DispatchGroup()
group.enter()
... code ...
group.leave
group.wait()
I use them to limit the number of http requests I send out in a batch, to wait for the response. Perhaps you could use them together with the suggestion in my comment. Watch this video too, dispatch groups in here, I think more.
https://developer.apple.com/videos/play/wwdc2016/720/
These simple classes helped me solve the problem.
class PushQueue {
internal var pushArray: Array<String> = [String]()
internal let pushQueue = DispatchQueue(label: "com.example.pushNotifications")
public func addPush(_ push: Push) {
pushQueue.sync {
if pushArray.contains(push.id) {
return
} else {
pushArray.append(push.id)
processNotification(push: push)
}
}
}
internal func processNotification(push: Push) {
PushInterface.sharedInstance.recievePrivatePush(push.userInfo as! [String: NSObject])
}
}
class CKPush: Equatable {
init(userInfo: [AnyHashable: Any]) {
let ck = userInfo["ck"] as? NSDictionary
let id = ck?["nid"] as? String
self.id = id!
self.userInfo = userInfo
}
var id: String
var userInfo: [AnyHashable:Any]
public static func ==(lhs: CKPush, rhs: CKPush) -> Bool {
return lhs.id == rhs.id ? true : false
}
}
Please ignore the sloppy force unwraps. They need to be cleaned up.

Resources