Tuesday, December 2, 2014

Don't mix Crashlytics and New Relic

We recently ran into an issue involving a very difficult crash. The issue was that the crash would only occur when the app had been archived and distributed. When we ran the app from Xcode to try to trace it, it would never occur.

Well, let's check the Crashlytics trace we thought. Except that it never came. I inserted "Crashlytics.sharedInstance().crash()" in an IBAction to make sure Crashlytics was even reporting. After a several hour delay, we finally had it come through.

Next we checked New Relic. We had one stack trace that didn't lead us in the right direction. We decided to try dsymbolicating a crash log off of the device. This simply led us to UIViewController.viewDidLoad(). As a last resort, my coworker started placing NSLog statements hoping to leave a trace for ourselves in the log file.

As he got a lead, Crashlytics finally gave us a trace. Both of them arrived at the same conclusion. New Relic was throwing a warning that it couldn't override the exception handler because other frameworks were trying to do so.

The moral of the story? Don't use more than one crash reporting framework if you can help it. Essentially, Crashlytics and New Relic were causing issues with each other but finding that out was a huge pain.

Monday, October 6, 2014

SourceKitService Crashed

Xcode occasionally starts crashing on me and it won't stop. Usually this is triggered by Xcode trying to actively compile my code while I'm typing. This crash triggers a cycle where it happens over and over and over. I get the message "SourceKitService Crashed Crashlog generated in ~/Library/Logs/DiagnosticReports Editor functionality temporarily limited." as picture below.



The autocomplete and text coloring in Xcode breaks for about five seconds. The coloring and autocomplete functionality return for about one second before the crash happens again.

I initially ran into this in the beta builds of Xcode 6 and hoped they would be addressed, but here we are with the official release and this is still an issue. At first I would assume there was an error in my code and that Xcode was crashing instead of showing me the error. I came to realized that the crash happened without rhyme or reason. I could rollback my changes to a known good build and the crash wouldn't go away.

The easiest way I've found to solve this is to delete the derived data. But simply deleting it from the organizer window in Xcode didn't work for me. You can stop the issue by navigating to the following folder.


~/Library/Developer/Xcode/DerivedData/

Simply deleting the projects folder wouldn't fix the problem for me (I think that's what Xcode is doing). I also have to delete the ModuleCache folder in order to get rid of the issue. This is certainly annoying, but at least bearable now that I can fix it.

Monday, August 25, 2014

User Defaults in Swift and iOS 8

I like to use a UserDefaultsManager class to handle all of the values I store in NSUserDefaults. Using computed properties in Swift makes it even easier to do. You can even make class level computed properties so that you don't need to bother with a Singleton or creating an instance of your UserDefaultsManager.


Thursday, August 21, 2014

Dynamically resizing labels in Swift

One thing that iOS does poorly is allowing dynamic layouts when it comes to text. When you create a label that can have variable length it's difficult to set layout constraints to allow that label to grow and shrink as needed. This is going to become an even bigger issue with the release of the iPhone 6. Screens will become wider so you can't count on a constant width anymore.

A solution that was often implemented in Objective-C was to do some calculation on the text in the label to find how much room it would need. After this computation, the frame would be updated so that it was able to show all of the text. Below is my approach to this issue in Swift.


I like to make sure my layout constraints are always right, so my method takes an NSLayoutConstraint for the height of the label. This keeps the label from shrinking back down if the screen is redrawn for some reason.

There are a few other things to note here. I am grabbing the font already set to the label because I have my desired font and color set in my StoryBoard. You could programmatically set those here if you prefer.

The other important thing to remember is when to call this. I call this method on my labels in viewDidAppear. The reason for this is because I want to use the width determined by my layout constraints. If you try to use frame.size.width before viewDidAppear, it won't be correct (it will be the same width as the StoryBoard has it laid out). This causes a redraw immediately when the screen is loaded. I'm currently looking into a way around this and will update this post.

UPDATE

The simplest way I found to get the layout constraints to take effect before the view appears is to call view.layoutIfNeeded() from viewWillAppear. This will layout the view according to the layout constraints for you. Then you can call resizeHeightToFit on your labels from viewWillAppear (after you call layoutIfNeeded) and your labels will resize before the view becomes visible to the user.

Tuesday, August 19, 2014

Unit Testing ViewControllers in Swift with iOS 8 and Xcode 6

I spent a good bit of time trying to figure out how to properly unit test ViewControllers. There are a few posts on StackOverflow but there was only one real answer I found (as of Xcode 6 Beta 6). I wasn't satisfied with that solution and decided to keep digging.

The problem that arises with Swift is that each target makes a module. The way you would test a ViewController in Xcode 5 would be to do something like this:



When you try this in Swift, you get a crash when casting the object returned from the instantiateViewControllerWithIdentifier method. This is because the storyboard is making a ViewController that is actually of type AppModule.MyViewController and you are trying to cast it as UnitTestModule.MyViewController.

The only solution I was able to find while searching was to make MyViewController public along with any methods and properties I wanted to access. I could then import my module into my test class with import AppModule (usually your app name).


The problem with this approach is that you either have to make everything public (which is really bad practice) or make getters and setters for everything you want to test (the whole advantage of properties is to have automated getters and setters).


The solution is actually quite simple. Assuming you have setup all of your ViewControllers and your StoryBoard to have membership of the test target along with the main target, you can simply use a StoryBoard that is part of your test bundle instead of part of the main target's bundle. So instead of using nil, use NSBundle(forClass: self.dynamicType). I ended up using this for my tests:



I think that this may have been the "correct" implementation before modules, but I didn't find anyone doing it this way: presumably because it didn't affect the tests.

 

© 2013 iOS Mike. All rights resevered. Designed by Templateism

Back To Top