1

Please help with this error. It has baffled me for a few days now. The error is happening only on a few selected devices, but I'm unable to simulate the behaviour on my devices.

I have an app that shows notifications to the user. The notification Navigation Controller has a label that uses the following class to set some attributes

class LabelPaddingNotifications: UILabel {
var insets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)


func padding(_ top: CGFloat, _ bottom: CGFloat, _ left: CGFloat, _ right: CGFloat) {
    self.frame = CGRect(x: 0, y: 0, width: self.frame.width + left + right, height: self.frame.height + top + bottom)
    insets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
}

override func drawText(in rect: CGRect) {
    super.drawText(in: rect.inset(by: insets))
}

override var intrinsicContentSize: CGSize {
    get {
        var contentSize = super.intrinsicContentSize
        contentSize.height += insets.top + insets.bottom + 50
        contentSize.width += insets.left + insets.right
        return contentSize
    }
}}

The app crashes on the following line sometimes. I'm unable to simulate the behavior on my devices or simulators though. The following code is in the viewDidLoad of the class

labelNotification.layer.cornerRadius = _smallButtonCornerRadius

And the outlet is defined as

@IBOutlet var labelNotification: LabelPadding!

Small radius is defined in a constants file as follows

let _smallButtonCornerRadius : CGFloat = 7

I also have an extension to CALayer as follows

extension CALayer {

func shake(duration: TimeInterval = TimeInterval(0.5)) {

    let animationKey = "shake"
    removeAnimation(forKey: animationKey)

    let kAnimation = CAKeyframeAnimation(keyPath: "transform.translation.x")
    kAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    kAnimation.duration = duration

    var needOffset = frame.width * 0.15,
    values = [CGFloat]()

    let minOffset = needOffset * 0.1

    repeat {

        values.append(-needOffset)
        values.append(needOffset)
        needOffset *= 0.5
    } while needOffset > minOffset

    values.append(0)
    kAnimation.values = values
    add(kAnimation, forKey: animationKey)
}

func pulsate() {
    let pulse = CASpringAnimation(keyPath: "transform.scale")
    pulse.duration = 0.5
    pulse.fromValue = 0.95
    pulse.toValue = 1.0
    pulse.autoreverses = false
    pulse.repeatCount = 0
    pulse.initialVelocity = 0.5
    pulse.damping = 1.0
    add(pulse, forKey: nil)
}
}

Crashlytics is showing me the following error

The stack trace indicates that heap corruption may have caused your app to crash. Memory corruption can occur pretty easily from freeing a dangling pointer, a thread race, or bad pointer arithmetic. The important thing to keep in mind is that the resulting crash may happen long after the initial corruption. As a result, the stack trace for this crash might not provide any clues to the location of the bug in your code. However, you can still fix memory issues with tools from Apple. For speedy resolution of memory corruption issues, we recommend regularly auditing your app with Xcode’s memory debugging facilities: Visual Memory Debugger, Zombies Instrument, Address Sanitizer, Thread Sanitizer and malloc diagnostics.

While Crashes in XCode organizer shows me an error on the line above giving me the following

enter image description here

Update

This particular View Controller is fired from the AppDelegate file upon receiving a local notificaton that I set in the app. The code for it is as below

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
                if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VCShowNotification") as? ShowNotificationVC {
                if let window = self.window, let rootViewController = window.rootViewController {
                    var currentController = rootViewController
                    while let presentedController = currentController.presentedViewController {
                        currentController = presentedController
                    }
                    let navController = UINavigationController(rootViewController: controller)
                    currentController.present(navController, animated: true, completion: nil)
                    //currentController.present(controller, animated: true, completion: nil)
                }
            }}

I've now updated the launch of the notification controller from my appdelegate by adding a 1 second delay to it and on the main thread. This now opens up the app, but the notification controller isn't showing up anymore on some devices. The app has stopped crashing though. I can't get to simulate the error on my devices, which is why I'm really struggling with this one.

ibyte
  • 463
  • 4
  • 17
  • Did you find your problem? If so what was it? – David H Jul 25 '20 at 09:50
  • I did not find it David. I'm still struggling with the error. I also tried running a memory leak test, but the findings were none. – ibyte Jul 27 '20 at 13:18
  • At this point the best way to solve this is for you to spend the time to create a separate project that has the guts of this in it, and some way to thrash it, so it crashes from time to time. Put it on Dropbox or equiv. With a bounty you will surely get an answer soon, maybe from me! No one can solve it based on your description (which was nicely written!). If my suggestions didn't help don't know otherwise what else to say. – David H Jul 27 '20 at 17:37
  • Thanks, David. I'll see if I can do this. I'm a one-man army, too many jobs to handle at once. – ibyte Jul 29 '20 at 07:10
  • Hi David, I'm inclined to believe that this crash is caused due to the lack of memory. I've checked my Crashlytics reports and it seems most users have around 100MB of free RAM. Some with 50MB or so. Would you reckon that this could be one of the issues? I've changed the code and removed the corner radius property. The error has now moved down to a place where I add a Google ad banner. It is still EXC_BAD_ACCESS KERN_INVALID_ADDRESS. – ibyte Jul 29 '20 at 09:34
  • WOW - when I test on my older devices there is 1G plus of free memory. When you use Xcode what is the average memory footprint of your app? – David H Jul 29 '20 at 10:57
  • Looking at the crash again, I see viewDidLoad then CALayer active - seems odd, there is no window to draw in at that point . Look closely at your viewDidLoad code. – David H Jul 29 '20 at 10:59
  • The memory footprint is from my actual users that I've obtained from Crashlytics in the firebase console. Also, I've removed the corner radius property as I've mentioned earlier, the error has moved down to the place where the Google Advert banner is added. If I attempt a do try catch block, i'm unable to catch the error. I'm still new to error testing in swift. How do you actually catch errors which do not throw proprietary errors? – ibyte Jul 29 '20 at 15:11
  • My suggestion - look at current free memory periodically, if less than 500 megs then don’t do anything special. – David H Jul 29 '20 at 17:30
  • @user2672052 have you been able to create a separate project for this? – Sylvan D Ash Jul 31 '20 at 11:42
  • 1
    I had similar issue getting an alert to show. You need a new Window object - see this link: https://stackoverflow.com/a/58295128/1633251 – David H Aug 05 '20 at 20:58
  • I'll have a look at this David. – ibyte Aug 06 '20 at 09:37

1 Answers1

2

Errors like these are often due to thread mismatch, meaning, this UI code is running in the main thread, but other code changes its parameters in a secondary thread.

You can add a observer and test for this:

// Note typing this in Stack so no way to see typos etc
@IBOutlet var labelNotification: LabelPadding! {
  didSet {
    assert(Thread.isMainThread)
    assert(LabelPadding != nil)
  }
}

There is a slight chance that the property is being read before set, so you might want to redefine it as:

@IBOutlet var labelNotification: LabelPadding?

Then in each usage use a guard:

guard let labelNotification = labelNotification else { return }
    
David H
  • 40,852
  • 12
  • 92
  • 138
  • Thanks David. I'll give it a go and see what happens. – ibyte Jul 20 '20 at 11:52
  • @user2672052 I have a feeling that David intuition is correct, and this is due to threading issues. If I remember corretly, notification is delivered on the thread/queue it was posted. Could you assure that the code you've posted in "Update" is running on the main thread? – Andrzej Michnia Aug 03 '20 at 06:22
  • Please see updated code in my question. I've updated the code in appdelegate, the app stopped crashing, it opens up, but never fires the viewcontroller and this is still affecting only about 9-10% of the devices. – ibyte Aug 03 '20 at 16:21