I have an app with about 30 active users and 500+ registered users and this issue is only happening with one person who is on 9.2 using an iPhone 6.
I can see from crash reports she's had over 60 crashes and she has said that the problem is OK sometimes and not others.
I have the same test device here and no issue is happening.
I can't get much out of the crash report details from fabric but the crash is happening at the following method and the app is crashing at the final else of this method, I can tell from her description of the problem:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
let bounds = UIScreen.mainScreen().bounds
var screenWidth = bounds.size.width
let screenHeight = bounds.size.height
if indexPath.row == 0 {
let font = UIFont(name: "Helvetica Neue", size: 17.0)
let heightCalc = heightForView(self.postDescription!, font: font!, width: screenWidth - 32)
if self.photoWidth != nil{
let imageAspectRatio = self.photoWidth!/self.photoHeight!
var imageViewFrameHeight = screenWidth/imageAspectRatio
if self.portBool {
if imageViewFrameHeight > (screenHeight - 64) {
screenWidth = screenWidth * 0.60
imageViewFrameHeight = imageViewFrameHeight * 0.60
}
}
return imageViewFrameHeight + 136 + heightCalc + 26
//return 557
}
else{
return 136 + heightCalc + 30
}
}
else if indexPath.row == 1 {
return 50.0
}
else if indexPath.row == 2{
return 40.0
}
else{
if self.commentsDetailed != nil && self.commentsDetailed!.count >= (indexPath.row - 3)
{
let commentsDetailed = self.commentsDetailed![indexPath.row-3]
let font = UIFont(name: "Helvetica Neue", size: 14.0)
let heightCalc = heightForView(commentsDetailed.comment!, font: font!, width: screenWidth - 51)
if commentsDetailed.authorId == Prefs.userId.description {
return 45 + heightCalc + 40 + 30
//return 131.5
}
else{
return 45 + heightCalc + 40
//return 101.5
}
}
else {
return 80.0
}
}
}
For those wondering, I need to calculate the height to avoid a jittery scroll which was happening when I was using the table view automatic dimension. So I just need to solve the issue within here and not switch approach.
Some info from crashlytics:
EXC_BREAKPOINT
Thread : Crashed: Thread
0 ProjectName 0x10008a64c specialized NewFeedDetailedController.tableView(UITableView, heightForRowAtIndexPath : NSIndexPath) -> CGFloat (NewFeedDetailedController.swift)
1 ProjectName 0x100085258 @objc NewFeedDetailedController.tableView(UITableView, heightForRowAtIndexPath : NSIndexPath) -> CGFloat (NewFeedDetailedController.swift)
2 UIKit 0x187683638 __66-[UISectionRowData refreshWithSection:tableView:tableViewRowData:]_block_invoke + 396
3 UIKit 0x187641818 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3948
4 UIKit 0x1876407d4 -[UITableViewRowData rectForFooterInSection:heightCanBeGuessed:] + 412
5 UIKit 0x187640590 -[UITableViewRowData heightForTable] + 64
6 UIKit 0x18764039c -[UITableView _updateContentSize] + 220
7 UIKit 0x1878a6f60 -[UITableView _rebuildGeometry] + 44
8 UIKit 0x1876462c8 -[UITableView didMoveToWindow] + 144
9 UIKit 0x18755705c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1496
10 UIKit 0x18757c568 -[UIScrollView _didMoveFromWindow:toWindow:] + 92
11 UIKit 0x187556d7c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 760
12 UIKit 0x187556310 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 152
13 Foundation 0x183189500 -[NSISEngine withBehaviors:performModifications:] + 168
14 UIKit 0x187556194 -[UIView(Hierarchy) _postMovedFromSuperview:] + 532
15 UIKit 0x187563b80 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1784
16 UIKit 0x187755600 -[_UIParallaxDimmingView didMoveToWindow] + 180
17 UIKit 0x18755705c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 1496
18 UIKit 0x187556d7c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 760
19 UIKit 0x187556310 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 152
20 Foundation 0x183189500 -[NSISEngine withBehaviors:performModifications:] + 168
21 UIKit 0x187556194 -[UIView(Hierarchy) _postMovedFromSuperview:] + 532
22 UIKit 0x187563b80 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1784
23 UIKit 0x1877f5db4 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke_2 + 1656
24 UIKit 0x18756a964 +[UIView(Animation) performWithoutAnimation:] + 80
25 UIKit 0x187755118 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 260
26 UIKit 0x187870840 +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:] + 220
27 UIKit 0x187754c90 -[_UINavigationParallaxTransition animateTransition:] + 1060
28 UIKit 0x18770e6a0 -[UINavigationController _startCustomTransition:] + 3544
29 UIKit 0x18761a9b8 -[UINavigationController _startDeferredTransitionIfNeeded:] + 688
30 UIKit 0x18761a694 -[UINavigationController __viewWillLayoutSubviews] + 60
31 UIKit 0x18761a5fc -[UILayoutContainerView layoutSubviews] + 208
32 UIKit 0x187557778 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 656
33 QuartzCore 0x184f66b2c -[CALayer layoutSublayers] + 148
34 QuartzCore 0x184f61738 CA::Layer::layout_if_needed(CA::Transaction*) + 292
35 QuartzCore 0x184f615f8 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
36 QuartzCore 0x184f60c94 CA::Context::commit_transaction(CA::Transaction*) + 252
37 QuartzCore 0x184f609dc CA::Transaction::commit() + 512
38 UIKit 0x18754dc78 _afterCACommitHandler + 180
39 CoreFoundation 0x182820588 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
40 CoreFoundation 0x18281e32c __CFRunLoopDoObservers + 372
41 CoreFoundation 0x18281e75c __CFRunLoopRun + 928
42 CoreFoundation 0x18274d680 CFRunLoopRunSpecific + 384
43 GraphicsServices 0x183c5c088 GSEventRunModal + 180
44 UIKit 0x1875c4d90 UIApplicationMain + 204
45 ProjectName 0x1001663b0 main (AppDelegate.swift:21)
46 libdyld.dylib 0x1822ee8b8 start + 4
And the heightForView func:
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRectMake(0, 0, width, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
Updated Code with checks for nil, still crashing for the user:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
let bounds = UIScreen.mainScreen().bounds
var screenWidth = bounds.size.width
let screenHeight = bounds.size.height
if indexPath.row == 0 {
CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["returning 50.0 here"]))
let font = UIFont(name: "Helvetica Neue", size: 17.0)
if let description = self.postDescription {
let heightCalc = heightForView(description, font: font!, width: screenWidth - 32)
if self.photoWidth != nil && self.photoHeight != nil {
CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["inside photowidth is not nil"]))
let imageAspectRatio = self.photoWidth!/self.photoHeight!
var imageViewFrameHeight = screenWidth/imageAspectRatio
if self.portBool {
if imageViewFrameHeight > (screenHeight - 64) {
screenWidth = screenWidth * 0.60
imageViewFrameHeight = imageViewFrameHeight * 0.60
}
}
return imageViewFrameHeight + 136.0 + heightCalc + 26.0
//return 557
}
else{
CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["when photowidth is nil"]))
return 136.0 + heightCalc + 30.0
}
}
else{
CLSLogv("crashing in indexPath.Row = 0 %@", getVaList(["unable to unwrap self.postDescription"]))
if self.photoWidth != nil && self.photoHeight != nil {
let imageAspectRatio = self.photoWidth!/self.photoHeight!
var imageViewFrameHeight = screenWidth/imageAspectRatio
if self.portBool {
if imageViewFrameHeight > (screenHeight - 64) {
screenWidth = screenWidth * 0.60
imageViewFrameHeight = imageViewFrameHeight * 0.60
}
}
return imageViewFrameHeight + 136.0 + 26.0
}
else{
return 136.0 + 30.0
}
}
}
else if indexPath.row == 1 {
CLSLogv("crashing in indexPath.Row = 1 %@", getVaList(["returning 50.0 here"]))
return 50.0
}
else if indexPath.row == 2 {
CLSLogv("crashing in indexPath.Row = 2 %@", getVaList(["returning 40.0 here"]))
return 40.0
}
else{
if self.commentsDetailed != nil && self.commentsDetailed!.count > (indexPath.row - 3)
{
if let commentsDetailed = self.commentsDetailed?[indexPath.row-3]{
let font = UIFont(name: "Helvetica Neue", size: 14.0)
if let comment = commentsDetailed.comment{
let heightCalc = heightForView(comment, font: font!, width: screenWidth - 51)
if commentsDetailed.authorId == Prefs.userId.description {
return 45.0 + heightCalc + 40.0 + 30.0
}
else{
return 45.0 + heightCalc + 40.0
}
}
else{
if commentsDetailed.authorId == Prefs.userId.description {
return 45.0 + 40.0 + 30.0
}
else{
return 45.0 + 40.0
}
}
}
else {
CLSLogv("in the else for defining commentsDetailed %a",getVaList(["commentsdetailed"]))
return 80.0
}
}
else {
CLSLogv("in the condition for checking commentsDetailed = nil - true %a", getVaList(["CRASH RELATED TO RETURNING 80 IF ARRAY IS NIL?"]))
return 80.0
}
}
}
The error is array out of bounce at the last else closure like you said:
If for example the array is size 1, then the last element you can index is arr[0] but count >= (indexPath.row - 3) will let indexing arr[1] which will cause the error.
There are also other fixes you can do to optimize since this method is called repeatedly. (I'd suggest maybe having the fonts and other variables outside so that it doesn't create them each time. There maybe such optimization behind but I'm not sure).
I had to re-write the whole thing playground to find the problem so I guess I'll just paste the code here :)
there is still a little bit of inconsistency in your code. try to distinguish between
self.comentsDetailed
andcomentsDetailed
in you code. try to choose another name for the local valuelet cd = self.commentsDetailed![indexPath.row-3]
for an example, and next use this value where it is necessary in next statements. this can help you to see what wrong happens in your code.I think the only possible point where the second else can fail is the
commentsDetailed.comment!
unwrapping, which should fail in case it were equal to nil. The rest seems perfectly robust, including theheightForView
method. If checkingcommentsDetailed.comment != nil
doesn't do the trick, I'll start thinking it's some random 9.2 early bug...