I do push notifications. If there is no data in the database, they downloaded before display. I did a time interval using Grand Central Dispatch, but it is not right because for example if the user is a weak Internet application falls.
How delay with barrier of GCD the display of the data before download data?
This code with time interval:
if equal == false {
let url = "https://****.**/json/post/\(data)"
self.download.getLastestNews(url)
}
let when = DispatchTime.now() + 2
DispatchQueue.main.asyncAfter(deadline: when) {
let newsCategory = self.realm.objects(News.self).filter("newsID == \(self.id)")
vc.titleText = (newsCategory.first?.newsTitle)!
vc.fullText = (newsCategory.first?.newsFullText)!
vc.imageLink = (newsCategory.first?.newsImage)!
if let tabBarController = self.window?.rootViewController {
tabBarController.present(vc, animated: true, completion: nil)
}
}
Platform iOS 9.*, iOS 3
Delaying the queue won't always work because as you said, sometimes Internet connection is too slow and there might be no data received, so the program may fail with error. While downloading anything from Internet, check for completion handling methods. Inside them use the code you gave in DispatchQueue closure.
Related
I'm working on an iOS app project that involves Firebase's phone authentication. I have it working fine on simulator, my iPhone, and my iPad. However, now that I am in the TestFlight stage , my external testers are experiencing long delays in receiving their OTPs as well as receiving duplicates when they reach the ViewController where they enter the OTP code (This is probably due to them hitting the button multiple times).
I also have APNs enabled and working properly.
I don't have much code to share as I followed Firebase's documentation.
What could be some reasons for a long delay in receiving the OTP code from Firebase? I will be including an activity spinner in the project when users tap the sign-in button. However, I also don't want it to be spinning for a minute as users wait for their OTP.
#objc func phoneSignIn() {
guard let phoneNumber = startVerificationView.phoneNumberTextField.text else { return }
let completePhoneNumber = "+1\(phoneNumber)"
Auth.auth().settings?.isAppVerificationDisabledForTesting = isVerificationDisabled
PhoneAuthProvider.provider().verifyPhoneNumber(completePhoneNumber, uiDelegate: nil) { (verificationId, error) in
if error == nil {
guard let verifyId = verificationId else { return }
UserDefaults.standard.set(verifyId, forKey: "verificationId")
let vc = CheckVerificationViewController()
vc.modalPresentationStyle = .fullScreen
vc.completePhoneNumber = completePhoneNumber
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
Also isVerificationDisabled is set to false.
I use GCD DispatchWorkItem to send some data to Firebase. I first send a pic to Firebase Storage and receive the metadata in the completion handler and turn it into a url and then into a string. I then send that url string to Firebase Database. I've tested my GCD method while going to the background, commenting out the firstCall so that it's problemCall fires, and also commenting out the secondCall so that it's problemCall fires etc and everything works fine. However I'm unsure of what to do if the app stops while one of network calls are being performed because the user shuts off the phone.
I'll explain it in steps:
The user presses a sendButton, an image gets initialized, then converted into data. I have a global flag that gets sets to true when the button is pressed. It was initially set to false and gets set back to false if there is a problem or if it reaches the callback of the secondCall.
The firstCall is a DispatchWorkItem(DWI) that take the data and sends it to FB Storage.
At the end of the firstCall I call firstCall.perform() so it can run. Right below that I initialize a problemCall as a DWI that fires off in 10 secs. If it fires it will cancel the firstCall, change the global flag to false, and show an error alert.
Inside the callback for the firstCall, if it's reached successfully I cancel the problemCall.
Assuming the firstCall is completed in less then 10 seconds I then initialize a secondCall as a DWI.
Inside the secondCall I take the url from the firstCall and send it to the FB Database.
At the bottom of the secondCall I call secondCall.perform() so it can now run. Right below that I again initialize the problemCall with another DWI that will fire off in 20 secs. If it fires off it will cancel the secondCall, change the global flag to false, and show an alert
Inside the callback for the secondCall, if it's reached successfully I cancel the problemCall and then self.wereGoodNowDoSomething() runs, and change the globe class to false.
AClass:
let storageRef = myFbStorageRef()
let dbRef = myFbDatabaseRef()
var firstCall: DispatchWorkItem?
var secondCall: DispatchWorkItem?
var problemCall: DispatchWorkItem?
var myImage: UIImage?
var imageUrlStr: String?
var someGlobalCass: SomeGlobalClass()
#IBAction fileprivate func sendButtonPressed(_ sender: UIButton){
self.someGlobalClass.flag = true
self.myImage = someFuncReturnsSomeImage()
if let convertImage = self.myImage, let imageData = UIImageJPEGRepresentation(convertImage, 0.2){
self.firstCall = DispatchWorkItem{ [weak self] in
self?.storageRef.putData(imageData, metadata: nil, completion: {
(metadata, error) in
//if it gets here in less then 10 secs cancel the problemCall for the firstCall
self.problemCall?.cancel()
if let url = metadata?.downloadURL()?.absoluteString{
self.imageUrlStr = url
}
self.secondCall = DispatchWorkItem{ [weak self] in
var imageDict = [String:Any]()
imageDict.updateValue(self.imageUrlStr, forKey: "imageUrlStr")
self?.dbRef.updateChildValues(imageDict(), withCompletionBlock: {
(error, ref) in
//if it gets here in less then 10 secs cancel the problemCall for the secondCall
self.problemCall?.cancel()
//if it reaches here were good
self.wereGoodNowDoSomething()
self?.someGlobalClass.flag = false
}
}
/*>>>>>>APP GETS SHUT HERE DOWN BEFORE secondCall CAN PERFORM<<<<<<*/
self.secondCall?.perform()
//if the secondCall doesn't complete in 20 secs then cancel it
self.problemCall = DispatchWorkItem{ [weak self] in
self?.secondCall?.cancel()
self?.someGlobalClass.flag = false
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 20, execute: self.problemCall!)
}
self.firstCall.perform()
//if the firstCall doesn't run in 10 secs then cancel it
self.problemCall = DispatchWorkItem{ [weak self] in
DispatchQueue.main.async {
self?.firstCall?.cancel()
self?.someGlobalClass.flag = false
//show alert for error
return
}
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: self.problemCall!)
}
}
How do I handle the app getting shut down between steps 6 and 7 while the firstCall completes, the secondCall is running but it never completes and the problemCall never fires because the app is shut down?
The issue is I now have image data sitting inside FB Storage, the url string never got sent to the database, and the global flag will now be set to true when it never reached a point to change it back to false.
Btw in the callback I excluded the if error != nil. I'd cancel the problemCall there, set the global flag back to false, and then show an alert.
I don't really understand when messageLostHandler is triggered on the subscriptionWithMessageFoundHandler method.
This is my code:
func suscribeToNearbyDevices(myUserId: String){
subscription = messageMgr?.subscription(messageFoundHandler: { (message: GNSMessage?) in
if let incomingMessage = message, let content = incomingMessage.content {
if let userIdEncoded = String(data: content, encoding: String.Encoding.utf8) {
NotificationCenter.default.post(name: Notification.Name(rawValue: CommunicationVariables.newUserNotificationKey), object: nil,
userInfo:userIdEncoded)
}}, messageLostHandler: { (message: GNSMessage?) in
if let incomingMessage = message, let content = incomingMessage.content {
if let userIdEncoded = String(data: content, encoding: String.Encoding.utf8) {
NotificationCenter.default.post(name: Notification.Name(rawValue: CommunicationVariables.exitUserNotificationKey), object: nil,
userInfo: [CommunicationVariables.userIdNotificationField:userIdEncoded])
}
}
}, paramsBlock:{(params:GNSSubscriptionParams?) in
guard let params = params else { return }
params.strategy = GNSStrategy(paramsBlock: { (params: GNSStrategyParams?) in
guard let params = params else { return }
params.allowInBackground = true
})
})
}
I have two iphones, If I have the two apps on the foreground, they can see each other. When I press home in one, the messageLostHandler is triggered, but if I walk out of range (like outside-of-the-house-out-of-range) the messageLostHandler is never triggered.
Why? What cause the messageLostHandler to be triggered?
Thanks!
I see that your subscription is configured to work in the background, but is the publication also configured for background? If not, then when you press the Home button on the publishing device, the app is backgrounded, causing the publication to be disabled, and the subscriber receives a call to the messageLostHandler block.
Regarding the out-of-range problem: BLE (Bluetooth Low Energy) works at a very high range (up to 100m outdoors, and less when there are obstacles). So you have to walk very far for the devices to be completely out of range. An alternative is to place one of the devices inside a fully closed metal box (a faraday cage), which blocks all radio transmissions. Within 2-3 minutes, the subscriber should receive a call to the messageLostHandler block. (Incidentally, the 2-3 minute timeout is rather long, and we're have a discussion of whether we should shorten it.)
Let me know if you need more help figuring out what's going on.
Dan
I'm using swift perfect 2.0 and i need to call a function after 10 seconds. I can make it work on a normal iOS application with this code:
let when = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: when){
//call function
}
But i can't do this in swift perfect, and i don't know how to work arround. This is the structure of my request:
public func InsertPost(_ request: HTTPRequest, response: HTTPResponse)
//logic
response.status = .custom(code: 200, message: "Success!")
response.completed()
//here i want to send a notification to some users, but only after 10 seconds.
//So i try to call function sendNotifications() like this:
let when = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: when){
sendNotifications()
}
{
It never calls sendNotifications(), even if i place it before response.completed(), and i'm probably thinking wrong. So my question is, is there any other way to use Dispatchqueues in perfect 2.0? They don't seem to work.
Ok, now i understand. I can't block main queue in swift perfect.
solution:
let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background, target: nil)
let when = DispatchTime.now() + 10
backgroundQueue.asyncAfter(deadline: when){
//call function
}
It looks like perfect 2.0 has it's own thread management setup. Checkout this link:
http://perfect.org/docs/thread.html
I am using a Particle Core to get the temperature from my room. The temperature is accessed through the cloud, which is being constantly updated in a variable. This is how I access the variable and display it:
func updateTemp(){
let seconds = 3.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.myPhoton?.getVariable("tempF", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let _ = error {
print("Failed reading temperature from device")
}
else {
if let larry = result as? Int {
self.temp.text="\(larry)˚"
self.truth++ //Once a value has been found, update the count.
}
}
})
})
}
override func viewDidLoad() {
sparkStart()
}
override func viewDidLayoutSubviews() {
updateTemp()
NSTimer.scheduledTimerWithTimeInterval(100.0, target: self, selector: "updateTemp", userInfo: nil, repeats: true) //Gaurantees that the app is updated every 100 seconds. That way we have a fresh temperature often.
//Stop the spinning once a value has been found
if truth == 1{
activity.stopAnimating()
activity.removeFromSuperview()
}
}
Since this is my Particle Core detecting the temperature from environment, the temperature variable is constantly changing. However, when I use NSTimer, the code does not get updated in the time specified. Instead, it begins by updating based on the specified time, but then the time starts decreases exponentially and the variable is updated every 0.001 seconds or so. Any thoughts?
Im assuming what we see is not the full code. In your viewDidLayoutSubviews function, you call updateTemp twice. Once explicitly and once via timer callback.
Your updateTemp function schedules the network call in the main run loop, that's where the timer is also running. The dispatch_after function queues the execution of the readout updates one after the other. I am now assuming, that something in your display code causes repeated triggers of viewDidLayoutSubviews, each of which schedules two new updates etc. Even if the assumption is false (there are a couple of other possibilities due to network code being slow and the timer also running in the main run loop), I am guessing if you drop the explicit call to updateTemp you'll lose the "exponential" and should be fine.
In general, as the web call is largely asynchronous, you could just use the timer and call your sensor directly or if you feel GCD has an important performance advantage switch to dispatch_async and apply for the next available queue with each call via calling dispatch_get_global_queue