swift app battery very high and cpu min. 105% [closed] - ios

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
My swift 2 app have a very hight battery usage and a min. cpu of 105%
but first not the whole time.
I have an table view controller.
From there you can go to an camera controller - there you have to scan a barcode.
If the barcode was detected, a new view controller send the barcode via http post request to my mysql database and get informations.
this informations I will send to the last view controller. (this is my problem view controller, because there my cpu goes to min. 105% and the battery usage will be very high. from now in my complete app my cpu will be the whole time on min 105% - no matter which view controller will be the next.)
This is only a simple static table view, where the informations (2 Strings) will be shown. there you can click on "Save" and your entry will shown in the first table view controller.
I have delete all my code for the last view controller to make it blank.
no functions will be active in this controller.
But the cpu will goes up, too.
Any idea how can i check why the cpu goes up?
UPDATE
in my http post request controller (3)
is this code:
override func viewWillAppear(animated: Bool) {
self.LoadingCircle.rotate360Degrees(completionDelegate: self)
self.isRotating = true
}
rotate360Degress Extension
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 2.5, completionDelegate: AnyObject? = nil) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI * 2.0)
rotateAnimation.duration = duration
if let delegate: AnyObject = completionDelegate {
rotateAnimation.delegate = delegate
}
self.layer.addAnimation(rotateAnimation, forKey: nil)
}
}
if i delete the code in my viewWillAppear part, the cpu will stay "normal"

The delegate in your case is unnecessary, actually it will cause this code to run in multiple instances, what will rise your CPU usage.
If you want to repeat the event, you can set the .repeatCount = .infinity

Related

Dispatch Group Multiple Joined Swift [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
Hi everyone i have a question about a problem that i would like to do in swift and to see the best possible way to do that. I currently have an approach that i don't like and would be good if i can get some ideas about it.
Here it is the problem:
DispatchQ serial which is started
Then another Thread starts and waits for a completion block that returns 1 element per run
That thread will run multiple times and it will give 1 element each time until it returns all elements
The code shouldn't proceed forward and has to wait for all the elements to be collected in a array
After the array is full then the code can proceed forward.
Example with LocationManager:
Location manager starts fetching location
It output the location each time it finds a new location
At the end i have to collect all the locations i have gotten in 10 sec for example
Then proceed the work of the current thread
Example code:
var queue = DispatchQueue("q")
queue.async {
var locationCollected = Array<Location>()
// this will output multiple times
getLocation { location in
// add location to the collection
locationCollected.append(location)
}
// the current Q need to wait until all results are collected
return locationCollected
}
func getLocation() -> Location {
// this is the function that will return location
self.manager?.location(completion: { location in
// will provide location and update
completion(location)
}
}
Thank you much appreciated the help
If you want to adopt a linear flow of code approach then you can adopt the new async/await construct but that will limit your iOS versions.
In general blocking threads is best avoided as it can cause deadlocks. You should use completion handlers to get your asynchronous result rather than blocking.
For the purposes of this answer I will stick with the approach in your question.
Based on the requirements in your question, let's create a function that:
Fetches locations for a specified duration
Only fetches one location at a time
Provides the fetched location in an array
Is, itself, an asynchronous function
We need to ensure only a single location fetch is active at a time, which we can do with a DispatchSemaphore
func getLocations(for duration: TimeInterval, completion: (([Location])->Void)) {
var queue = DispatchQueue("q")
queue.async {
let semaphore = DispatchSemaphore(0)
var locationCollected = [Location]()
let startTime = Date()
while (startTime.timeIntervalSinceNow > -duration) {
getLocation { location in
locationCollected.append(location)
semaphore.signal()
}
semaphore.wait()
}
completion(result)
}
}

Swift: Animating a view (for in-app notifications) onto the screen for a few seconds before removing

I am attempting to do what is described perfectly here: https://github.com/bryx-inc/BRYXBanner
I want to create a banner that pops down on the screen for a few seconds before being removed (or removed when it is tapped on). The above project is great up until iOS 9. After that and with iOS 10, the banner no longer works as predicted and either shows itself without an animation for a third of a second or it doesn't show.
How can I add a view that animates onto the screen and then back off to provide a user a quick "No Internet" notification in-app. I want to avoid using the notification center.
I had a similar problem and created my own library for it: MDNotificationView
The example app on GitHub implements your idea. Here is a small snippet implementing it:
let view = MDNotificationCompactLayoutView()
view.textLabel.text = "No internet connection."
let notificationView = MDNotificationView(view: view)
notificationView.delegate = self
notificationView.show()
// MARK: - Notification View Delegate
func notificationDidShow(notificationView: MDNotificationView) {
// Hide the notification view automatically after 5 seconds.
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
notificationView.hide()
}
}

Being a good citizen displaying MKRouteSteps

In lack of a better title...
I'm am writing a custom turn-by-turn implementation in an app used for tracking as well as giving directions using CLLocation and MKMapView.
Getting a route for a given address, displaying it with the returned polyline, getting an ETA and a distance and displaying those all seems fairly trivial and easily implemented. One thing that there isn't a lot of guidance on though, is how to display MKRouteStep's returned in the MKRoute object. All guides online either don't bother mentioning them, or skip over them fairly easily by simply displaying them at once in a table view. Obviously not the most gracious solution, and in my opinion a critical part that's missing.
One solution I did find (can't find the SO answer I saw it in) mentioned creating a CLRegion for each step. So I wen't ahead and created my own implementation of it:
private func drawRouteOnMap() {
viewModel.getRouteForDelivery() { route in
if let route = route {
self.polyline = route.polyline
self.mapView.addOverlay(route.polyline, level: .AboveRoads)
//Add the route steps to the currentRouteSteps array for later retrieval
self.currentRouteSteps = route.steps
//Set the routeStepLabels text property to the first step of the route.
if let step = route.steps.first {
self.routeStepLabel.text = step.instructions
}
//Iterate over the steps in the `MKRoute` object. Create each region to monitor with the identifier set to an Int
var i = 0
for step in route.steps {
let coord = step.polyline.coordinate
let region = CLCircularRegion(center: coord, radius: 20.0, identifier: "\(i)")
self.locationManager.startMonitoringForRegion(region)
i += 1
}
}
}
}
Then I can listen to the func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) {} delegate method
private func updateUIForRegion(oldRegion:CLRegion) {
if let regionID = Int(oldRegion.identifier),
steps = currentRouteSteps {
//Fetch the step for the next region
let step = steps[regionID + 1]
routeStepLabel.text = step.instructions
}
}
Then when we exit the view controller
#IBAction func shouldDismissDidPress(sender: AnyObject) {
for (_, region) in locationManager.monitoredRegions.enumerate() {
locationManager.stopMonitoringForRegion(region)
}
dismissViewControllerAnimated(true, completion: nil)
}
This all works fairly well actually. But where things start to fall apart is if a user presses the home button when in the turn by turn navigation VC or if the app crashes. The app will not stop monitoring the regions set up until the locationManager is told to stop monitoring for them, even if the app is force closed. I wouldn't want the app to terminate all the region monitoring anyways, as the navigation wouldn't work when the user resumes the app. It just feels like the region monitoring isn't really the correct way to display the MKRouteStep's, but I'm out of ideas how to do it in another way.
Any ideas / better implementations are welcomed
So, first, the way that works is by definition the most elegant way - so if you have some code that handles every possibility, then by all means go for it!
With that in mind, for cases where there isn't a frank crash but your app is closed could you use one of the many delegate calls in AppDelegate to cease your monitoring?

Swift - slowing down "too fast" animation (UIImage updates) -- aka is NSTimer the only option here?

Newbie to IOS programming - learning through Swift. I'm writing a simple "slot machine / dice game".
I'm trying to show the user a flashing sequence of rolls before the "actual" roll appears.
func doFancyDiceRoll() {
for x in 1...100 {
Die1.image = PipsImg[RollOne()]
Die2.image = PipsImg[RollOne()]
Die3.image = PipsImg[RollOne()]
}
}
Die1, etc., are defined as generic UIImage views.
In any case, I'm not seeing the 100x iterations, just the images at the end of the loop. I'm assuming that either it redraws "too fast" or that IOS is trying to be smart, and only draws the last images so as to conserve resources.
I'll wildly guess that I need to either implement some kind of delay here, or, IOS needs to be told to explicitly draw out my images, and not try to outthink my intent.
For the delay, I've seen something about NSTimer, but nothing I saw seems to simply say something like "pause for .05" second, and the whole construct was unclear as they were ObjC examples/conversions.
(Note: I've simplified things here --- normally, I would store the value of RollOne() so I can use it later. I also would like to make an array (or collection?) like Die[1].image, but that is another question.)
========== Edit =======
OK, so I'm following up with more of my original code, merged in with that of #doctordoder so we can discuss a bit better. Hopefully that is kosher. (If this appended question is too long, please advise me on the best way to post a lengthy follow-up directly.)
import UIKit
class ViewController: UIViewController {
//( stripping out stuff unneeded for discussion )
// refers to same label below - works but kosher??
#IBOutlet var btnRoll_x: UIView
#IBAction func btnRoll(sender: AnyObject) {
triggerRoll()
}
var timer : NSTimer? = nil
var rolls : Int = 0
func triggerRoll() {
//hiding is bad UI, but until i know how to "disable & dim"
btnRoll_x.hidden = true
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: "doFancyDiceRoll", userInfo: nil, repeats: true);
}
func doFancyDiceRoll() {
Die1.image = PipsImg[randomInt(6)]
Die2.image = PipsImg[randomInt(6)]
Die3.image = PipsImg[randomInt(6)]
if (++rolls > 10)
{
timer?.invalidate()
timer = nil
rolls = 0 // DCB added this presumed missing line
btnRoll_x.hidden = false //again, need to do this better
}
}
}
Hopefully, my implementation of the code is what would have been intended. I made some minor adjustments for (hopeful) clarity.
Good news is I have working code. I have just enough understanding to get in place, but I'm fuzzy on some details.
Here is what I (think I) know...
We declare an NSTImer object, and a roll counter at the main level of the class. I note that in my original version, I had the roll counter scoped within the rolling function itself. Took me a while to understand why it could not live in the DiceRoll loop itself, but now I do. I'm going to express it poorly, but since the timer is going to call DiceRoll multiple instances, it needs to live outside the function.
The button btnRoll gets touched, and invokes triggerRoll().
To prevent the user from touching the button while we are in progress, which put us into a state where the roll counter never got to zero, I hide the button. (I'll figure how to properly put in in disabled state later.)
The timer is set. It fires every .1 second (within limits), and is set to repeat. (until .invalidate ). And it "calls" the function doFancyDiceRoll via the selector: attribute.
So, the big change as previously noted is that doFancy..Roll() no longer loops. It excites a single instance up updating the images. It checks the counter, and if we reach the limit, we kill the timer, which stops the timer (invalidate). (And I unhide the button, making it available again.)
So, a few things I am concerned/wondering about: I get the value of timers for other things that need to happen periodically (take health away every second, check a GPS position every 10 seconds, etc.). It's seems a odd construct to force a screen refresh.
Frankly, I would have expected to see see something like this:
func doFancyDiceRoll() {
for x in 1...100 {
Die1.image = PipsImg[RollOne()] // and 2 and 3 of course.....
VIewController.forceRedraw <<=== something like this, or maybe
ViewController.wait(.05) <<== this?? I dunno ;-)
}
}
instead we end up with about 20 extra lines or so. I'd be interested in knowing if there other approaches that could work keeping the loop intact.
Anyway, assuming this is the one true way to go, I guess my followup to this is how do I pass parameters, since this is not a "real" function call. Trying
selector: "doFancyDiceRoll(40)"
was not objected to by the IDE, but failed in execution.
I had exactly same problem back in days, entire loop is finished before the view is refreshed as #doctordoder mentioned. Solved with using NSTimer
var rollCount = 0
let arrayOfImages = ["image01", "image02", "image03"]
var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("doFancyDiceRoll"), userInfo: nil, repeats: true)
func doFancyDiceRoll() {
if rollCount == 100 {
timer.invalidate
rollCount = 0
{
else {
//get images from array arrayOfImages[rollCount]
rollCount++
}
}
there could be typos, since I have no Xcode right now.
I have basically the same answer as above :(, but I thought I'd post it anyway.
var timer : NSTimer? = nil
var rolls : Int = 0
func doFancyDiceRoll() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: "roll", userInfo: nil, repeats: true);
}
func roll() {
println("rolling")
Die1.image = PipsImg[RollOne()]
Die2.image = PipsImg[RollOne()]
Die3.image = PipsImg[RollOne()]
if (++rolls > 100)
{
timer?.invalidate()
timer = nil
}
}
Rather than NSTimer and invalidating, you can use dispatch_after to do the work for you.
func rollDice(howManyTimes: Int) {
die1.image = PipsImg[RollOne()]
die2.image = PipsImg[RollOne()]
die3.image = PipsImg[RollOne()]
if howManyTimes > 0 {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) / 10.0))
dispatch_after(delayTime, dispatch_get_main_queue()) {
self.rollDice(howManyTimes - 1)
}
}
}
This will run the code for the number of times specified, delaying each time by 0.1 seconds. It works like this: First it sets the images on each die, then, if there are more iterations, it does a dispatch_after to call itself with rollDice(howManyTimes - 1)
With this, you don't need to maintain a NSTimer and it is pretty self contained.

My iOS app freezes but no error appears

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.

Resources