First, a little context: my app runs in the background for getting locations, and location updates are active for what's below.
CMMotionActivityManager's queryActivityStarting call returns 0 activities when run in the background, but if my app is active it returns activities for the same time period (the activities exist - they just aren't being returned while in the background).
The startActivityUpdates function works in the background, but I just want the high level activity data from queryActivityStarting without the battery cost of using startActivityUpdates.
Am I mistaken and something else is going on? These 2 functions have very similar signatures, so I'm a little surprised they'd work differently.
Edit - asked to show my code.
Here's how I ask for activities - I tried without a delay and with various delays - Apple docs say activities might not be available for several minutes and I didn't know what that meant (anyone have specifics? maybe something else has to happen before activities are available?), so I tried waiting first:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(60 * 60), execute: {
ModeStorage.sharedInstance.saveModes(tripId, beforeStart, end)
})
Here's the code that queries - the last logging line shows a count of 0:
guard CMMotionActivityManager.isActivityAvailable() else { return Log.log("Activity N/A", true) }
CMMotionActivityManager().queryActivityStarting(from:fromDate, to:toDate, to:OperationQueue.main) { [weak self] (arr, err) -> Void in
guard var activities = arr else {return Log.log("modes n/a", true) }
Log.log("modes save (\(fromDate)-\(toDate): \(activities.count) in \(UIApplication.shared.applicationState.rawValue))", true)
...
}
Another edit: leaving this open as I'd love to use queryActivityStarting, but I switched my code to use startActivityUpdates and that seems to work without a problem.
Related
I have a question concerning asynchronous requests. I want to request data from different sources on the web. Each source might have the data I want but I do not know that beforehand. Because I only want that information once, I don't care about the other sources as soon as one source has given me the data I need. How would I go about doing that?
I thought about doing it with a didSet and only setting it once, something like this:
var dogPicture : DogPicture? = nil {
didSet {
// Do something with the picture
}
}
func findPictureOfDog(_ sources) -> DogPicture? {
for source in sources {
let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
dogPicture = data.getPicture()
}
}
task.resume()
}
}
sources = ["yahoo.com", "google.com", "pinterest.com"]
findPictureOfDog(sources)
However it would be very helpful, if I could just wait until findPictureOfDog() is finished, because depending on if I find something or not, I have to ask the user for more input.
I don't know how I could do it in the above way, because if I don't find anything the didSet will never be called, but I should ask the user for a picture then.
A plus: isWhatIWanted() is rather expensive, so If there was a way to abort the execution of the handler once I found a DogPicture would be great.
I hope I made myself clear and hope someone can help me out with this!
Best regards and thank you for your time
A couple of things:
First, we’re dealing with asynchronous processes, so you shouldn’t return the DogPicture, but rather use completion handler pattern. E.g. rather than:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
...
return dogPicture
}
You instead would probably do something like:
func findPictureOfDog(_ sources: [String], completion: #escaping (Result<DogPicture, Error>) -> Void) {
...
completion(.success(dogPicture))
}
And you’d call it like:
findPictureOfDog(sources: [String]) { result in
switch result {
case .success(let dogPicture): ...
case .failure(let error): ...
}
}
// but don’t try to access the DogPicture or Error here
While the above was addressing the “you can’t just return value from asynchronous process”, the related observations is that you don’t want to rely on a property as the trigger to signal when the process is done. All of the “when first process finishes” logic should be in the findPictureOfDog routine, and call the completion handler when it’s done.
I would advise against using properties and their observers for this process, because it begs questions about how one synchronizes access to ensure thread-safety, etc. Completion handlers are unambiguous and avoid these secondary issues.
You mention that isWhatIWanted is computationally expensive. That has two implications:
If it is computationally expensive, then you likely don’t want to call that synchronously inside the dataTask(with:completionHandler:) completion handler, because that is a serial queue. Whenever dealing with serial queues (whether main queue, network session serial queue, or any custom serial queue), you often want to get in and out as quickly as possible (so the queue is free to continue processing other tasks).
E.g. Let’s imagine that the Google request came in first, but, unbeknownst to you at this point, it doesn’t contain what you wanted, and the isWhatIWanted is now slowly checking the result. And let’s imagine that in this intervening time, the Yahoo request that came in. If you call isWhatIWanted synchronously, the result of the Yahoo request won’t be able to start checking its result until the Google request has failed because you’re doing synchronous calls on this serial queue.
I would suggest that you probably want to start checking results as they came in, not waiting for the others. To do this, you want a rendition of isWhatIWanted the runs asynchronously with respect to the network serial queue.
Is the isWhatIWanted a cancelable process? Ideally it would be, so if the Yahoo image succeeded, it could cancel the now-unnecessary Pinterest isWhatIWanted. Canceling the network requests is easy enough, but more than likely, what we really want to cancel is this expensive isWhatIWanted process. But we can’t comment on that without seeing what you’re doing there.
But, let’s imagine that you’re performing the object classification via VNCoreMLRequest objects. You might therefore cancel any pending requests as soon as you find your first match.
In your example, you list three sources. How many sources might there be? When dealing with problems like this, you often want to constrain the degree of concurrency. E.g. let’s say that in the production environment, you’d be querying a hundred different sources, you’d probably want to ensure that no more than, say, a half dozen running at any given time, because of the memory and CPU constraints.
All of this having been said, all of these considerations (asynchronous, cancelable, constrained concurrency) seem to be begging for an Operation based solution.
So, in answer to your main question, the idea would be to write a routine that iterates through the sources, and calling the main completion handler upon the first success and make sure you prevent any subsequent/concurrent requests from calling the completion handler, too:
You could save a local reference to the completion handler.
As soon as you successfully find a suitable image, you can:
call that saved completion handler;
nil your saved reference (so in case you have other requests that have completed at roughly the same time, that they can’t call the completion handler again, eliminating any race conditions); and
cancel any pending operations so that any requests that have not finished will stop (or have not even started yet, prevent them from starting at all).
Note, you’ll want to synchronize the the above logic, so you don’t have any races in this process of calling and resetting the completion handler.
Make sure to have a completion handler that you call after all the requests are done processing, in case you didn’t end up finding any dogs at all.
Thus, that might look like:
func findPictureOfDog(_ sources: [String], completion: #escaping DogPictureCompletion) {
var firstCompletion: DogPictureCompletion? = completion
let synchronizationQueue: DispatchQueue = .main // note, we could have used any *serial* queue for this, but main queue is convenient
let completionOperation = BlockOperation {
synchronizationQueue.async {
// if firstCompletion not nil by the time we get here, that means none of them matched
firstCompletion?(.failure(DogPictureError.noneFound))
}
print("done")
}
for source in sources {
let url = URL(string: source)!
let operation = DogPictureOperation(url: url) { result in
if case .success(_) = result {
synchronizationQueue.async {
firstCompletion?(result)
firstCompletion = nil
Queues.shared.cancelAllOperations()
}
}
}
completionOperation.addDependency(operation)
Queues.shared.processingQueue.addOperation(operation)
}
OperationQueue.main.addOperation(completionOperation)
}
So what might that DogPictureOperation might look like? I might create an asynchronous custom Operation subclass (I just subclass a general purpose AsynchronousOperation subclass, like the one here) that will initiate network request and then run an inference on the resulting image upon completion. And if canceled, it would cancel the network request and/or any pending inferences (pursuant to point 3, above).
If you care about only one task use a completion handler, call completion(nil) if no picture was found.
var dogPicture : DogPicture?
func findPictureOfDog(_ sources, completion: #escaping (DogPicture?) -> Void) {
for source in sources {
let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
let picture = data.getPicture()
completion(picture)
}
}
task.resume()
}
}
sources = ["yahoo.com", "google.com", "pinterest.com"]
findPictureOfDog(sources) { [weak self] picture in
if let picture = picture {
self?.dogPicture = picture
print("picture set")
} else {
print("No picture found")
}
}
You can use DispatchGroup to run a check when all of your requests have returned:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
let group = DispatchGroup()
for source in sources {
group.enter()
let task = URLSession.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
dogPicture = data.getPicture()
}
group.leave()
}
task.resume()
}
group.notify(DispatchQueue.main) {
if dogPicture == nil {
// all requests came back but none had a result.
}
}
}
I had some trouble formulating the Question title, please make an edit if you have a better alternative.
I have a Login procedure to which I add a timeout:
private func startTimeout() {
self.timeoutActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
[weak self] in
guard let weakSelf = self else {
return
}
if weakSelf.timeoutActive && !weakSelf.loggedIn {
weakSelf.onError("Login timed out")
}
}
}
I have designed the Login procedure so that if we for any reason need to log in again (for example after logging out, or after the login process determined that there missing or wrong credentials), we end up in the same instance of the class that performs the Login.
Now, as far as I see, we can never prevent a scheduled block from executing, only prevent it by using some flags, which is what I have done with the timeoutActive flag. This works like a charm.
However, I run into a problem if the second Login is timed exactly so that the Previous dispatch block executes after the new Login process is started (When the new login procedure is initiated, the timeoutActive flag is once again set to true). The new Login receives a Timeout that is not correct.
I have been thinking about different ways to solve it, and tried a few, but couldn't get any of them to work.
I had an idea to use performSelectorAfterDelay instead of GCD, which is cancellable, but not available in Swift (3).
I also played with the thought of having some unique block ID with a list of blocked block IDs - but it just seems overkill.
I also had an idea about comparing the current dispatch time (.now()) in the block with the original deadline (.now() + 20) and see if it matches, but I don't know how exact this deadline is and it feels unstable.
The only idea I'm left with is making some kind of Task like object around the Login procedure itself, with the timeout included and create a new instance of that task for different Logins. Seems like a bit of work and I'd prefer if I found an easier way.
Has anyone encountered this type of situation before and have a solution?
So this is what I made from Dan's comment on the Question:
private var timeoutBlock: DispatchWorkItem? = nil
private func startTimeout() {
self.timeoutBlock = DispatchWorkItem(block: { [weak self] in
guard let weakSelf = self else {
return
}
if !weakSelf.loggedIn {
weakSelf.onError("Login timed out")
}
})
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 20, execute: self.timeoutBlock!)
}
And using self.timeoutBlock?.cancel() when leaving the ViewController or completing the process. This works as expected!
Here is the relevant function in my ViewController:
#IBAction func findPeople(_ sender: Any) {
let center = CLLocation(latitude: myLocation.latitude, longitude: myLocation.longitude)
let radiusQuery = geoFireUserLocations!.query(at: center, withRadius: 0.05)
print("Start Looking for People")
_ = radiusQuery!.observe(.keyEntered, with: { (key: String?, location: CLLocation?) in
print(" ALERT: Found Someone Close : ",key!)
})
_ = radiusQuery?.observeReady({
print(" All initial data has been loaded and events have been fired!")
})
print("Done Looking for People")
//TODO: Based on the GeoFire result do something smart
}
The operation works, but the response comes in an unexpected order. I thought that the .observeReady call would return the initial set of results, but it does not return anything right away, and execution continues on to the line that prints "Done Looking for People".
Is there any way to have my function block until it gets an initial set of results?
Here's the output that I get:
Start Looking for People
Done Looking for People
ALERT: Found Someone Close : oSf00ex6SyMAwpF2NRxymyxxx123
All initial data has been loaded and events have been fired!
I was expecting:
Start Looking for People
ALERT: Found Someone Close : oSf00ex6SyMAwpF2NRxymyxxx123
All initial data has been loaded and events have been fired!
Done Looking for People
How can I create a function that blocks until it gets an initial result from GeoFire?
I'd like to send the request, get a result, then take the appropriate action based on the result. The way the app is currently behaving there does not seem to be a way to have it wait for a result.
Can anyone offer some insight and/or suggestion about how to make this work the way I'm describing? Is there some sort of blocking call that I could call or a synchronization method that I could be using to ensure that I get a result before deciding the next action?
Cheers!
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
Does any body know what I need to check if app freezes after some time? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread? is there any way to identify?
Launch your app and wait for it to freeze. Then press the "pause" button in Xcode. The left pane should show you what method is currently running.
Generally, it is highly recommended to perform on the main thread all animations method and interface manipulation, and to put in background tasks like download data from your server, etc...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
Source : http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
Set a break point from where the freeze occurs and find which line cause that.
Chances may be,Loading of large data,disable the controls,overload in main thread,Just find out where that occurs using breakpoints and rectify based on that.
I believe it should be possible to periodically check to see if the main thread is blocked or frozen. You could create an object to do this like so:
final class FreezeObserver {
private let frequencySeconds: Double = 10
private let acceptableFreezeLength: Double = 0.5
func start() {
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: self.frequencySeconds, repeats: true) { _ in
var isFrozen = true
DispatchQueue.main.async {
isFrozen = false
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + self.acceptableFreezeLength) {
guard isFrozen else { return }
print("your app is frozen, so crash or whatever")
}
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
}
}
Update October 2021:
Sentry now offers freeze observation, if you don't wanna roll this yourself.
I reached an error similar to this, but it was for different reasons. I had a button that performed a segue to another ViewController that contained a TableView, but it looked like the application froze whenever the segue was performed.
My issue was that I was infinitely calling reloadData() due to a couple of didSet observers in one of my variables. Once I relocated this call elsewhere, the issue was fixed.
Most Of the Time this happened to me when a design change is being called for INFINITE time. Which function can do that? well it is this one:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution is to add condition where the function inside of viewDidLayoutSubviews get calls only 1 time.
It could be that another view is not properly dismissed and it's blocking user interaction! Check the UI Debugger, and look at the top layer, to see if there is any strange thing there.