<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2022-09-29T09:24:46+00:00</updated><id>/feed.xml</id><title type="html">RayMix - 技之路，艺之道</title><subtitle>You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.</subtitle><entry><title type="html">New Home</title><link href="/blog/2021/05/10/new-home.html" rel="alternate" type="text/html" title="New Home" /><published>2021-05-10T22:10:00+00:00</published><updated>2021-05-10T22:10:00+00:00</updated><id>/blog/2021/05/10/new-home</id><content type="html" xml:base="/blog/2021/05/10/new-home.html">&lt;p&gt;从很久之前看到GitPages，就一直考虑搬家，今天终于实现了，花了整整一个周日，从 WordPress 完美迁移。&lt;/p&gt;

&lt;p&gt;临时使用了默认的模板，发现还挺简洁，做了一下简单的自定义，增加了分类，最主要的是可以通过 markdown 来写了。&lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><summary type="html">从很久之前看到GitPages，就一直考虑搬家，今天终于实现了，花了整整一个周日，从 WordPress 完美迁移。 临时使用了默认的模板，发现还挺简洁，做了一下简单的自定义，增加了分类，最主要的是可以通过 markdown 来写了。</summary></entry><entry><title type="html">Swing Bone - Unity</title><link href="/works/2017/08/09/swing-bone-unity.html" rel="alternate" type="text/html" title="Swing Bone - Unity" /><published>2017-08-09T17:08:00+00:00</published><updated>2017-08-09T17:08:00+00:00</updated><id>/works/2017/08/09/swing-bone-unity</id><content type="html" xml:base="/works/2017/08/09/swing-bone-unity.html">&lt;blockquote&gt;&lt;a href=&quot;https://www.assetstore.unity3d.com/cn/#!/content/90743&quot;&gt;&lt;img class=&quot;alignnone size-full&quot; src=&quot;/assets/2017/08/80d5392d-33eb-496f-95bf-6e9c9eebd1f1.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;Swing Bone 在角色的Bone或关节上应用简单的物理效果，只需一步便可实现头发、衣服、尾巴、胸部以及任何部件的自然摇动/飘动效果。&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;p&gt;来源： &lt;em&gt;&lt;a href=&quot;https://www.assetstore.unity3d.com/cn/#!/content/90743&quot;&gt;Swing Bone - 资源商店&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content><author><name>ray</name></author><category term="Works" /><category term="Bone" /><category term="Cloth" /><category term="Hair" /><category term="Swing" /><category term="Swing Bone" /><category term="Unity3D" /><summary type="html">Swing Bone 在角色的Bone或关节上应用简单的物理效果，只需一步便可实现头发、衣服、尾巴、胸部以及任何部件的自然摇动/飘动效果。</summary></entry><entry><title type="html">Mustang</title><link href="/blog/2016/01/31/mustang.html" rel="alternate" type="text/html" title="Mustang" /><published>2016-01-31T00:10:00+00:00</published><updated>2016-01-31T00:10:00+00:00</updated><id>/blog/2016/01/31/mustang</id><content type="html" xml:base="/blog/2016/01/31/mustang.html">&lt;p&gt;突然喜欢上一款车，没有理由！&lt;/p&gt;

&lt;!--more--&gt;
&lt;p&gt;&lt;img src=&quot;/assets/2016/01/img_1603.jpeg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/2016/01/img_1602.jpeg&quot;&gt;&lt;img src=&quot;/assets/2016/01/img_1602.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><summary type="html">突然喜欢上一款车，没有理由！</summary></entry><entry><title type="html">Mark Zuckerberg becomes the sixth-richest person on Earth</title><link href="/blog/2016/01/30/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth.html" rel="alternate" type="text/html" title="Mark Zuckerberg becomes the sixth-richest person on Earth" /><published>2016-01-30T09:23:00+00:00</published><updated>2016-01-30T09:23:00+00:00</updated><id>/blog/2016/01/30/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth</id><content type="html" xml:base="/blog/2016/01/30/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth.html">&lt;p&gt;The Facebook Inc. founder saw his fortune rise $6.2 billion in trading Thursday after the world’s biggest social network delivered another quarter of record revenue. That gives the 31-year-old a net worth of $47.6 billion at 1:15 p.m. in New York, enough to surpass the $45.9 billion holdings of brothers Charles and David Koch, according […]&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://barbrahmusambachamamumba.com/2016/01/28/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth/&quot;&gt;http://barbrahmusambachamamumba.com/2016/01/28/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth/&lt;/a&gt;&lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="Facebook CEO" /><category term="Facebook founder" /><category term="Mark Zuckerberg" /><summary type="html">The Facebook Inc. founder saw his fortune rise $6.2 billion in trading Thursday after the world’s biggest social network delivered another quarter of record revenue. That gives the 31-year-old a net worth of $47.6 billion at 1:15 p.m. in New York, enough to surpass the $45.9 billion holdings of brothers Charles and David Koch, according […] http://barbrahmusambachamamumba.com/2016/01/28/mark-zuckerberg-becomes-the-sixth-richest-person-on-earth/</summary></entry><entry><title type="html">Unity 在IOS9 下字体残缺</title><link href="/blog/2015/11/18/unity-ios9-font-error.html" rel="alternate" type="text/html" title="Unity 在IOS9 下字体残缺" /><published>2015-11-18T13:34:00+00:00</published><updated>2015-11-18T13:34:00+00:00</updated><id>/blog/2015/11/18/unity-ios9-font-error</id><content type="html" xml:base="/blog/2015/11/18/unity-ios9-font-error.html">&lt;p&gt;升级到最新的Unity4.6.8之后，虽然Unity声称解决了字体问题，但是我们在项目中还是遇到了奇怪的问题，IOS9设备每次第一次运行时部分字体会残缺，但是在切换场景后字体恢复正常显示，IOS8的设备一切正常。&lt;/p&gt;

&lt;p&gt;经过大量的测试后发现问题原来出在 Player Settings 中的 Graphics API选项上，之前选择了Open GL ES 2.0,修改为Automatic后问题解决。
&lt;!--more--&gt;&lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="IOS9" /><category term="Unity" /><summary type="html">升级到最新的Unity4.6.8之后，虽然Unity声称解决了字体问题，但是我们在项目中还是遇到了奇怪的问题，IOS9设备每次第一次运行时部分字体会残缺，但是在切换场景后字体恢复正常显示，IOS8的设备一切正常。 经过大量的测试后发现问题原来出在 Player Settings 中的 Graphics API选项上，之前选择了Open GL ES 2.0,修改为Automatic后问题解决。</summary></entry><entry><title type="html">Unity Text Effects</title><link href="/blog/2015/03/18/unity-text-effects.html" rel="alternate" type="text/html" title="Unity Text Effects" /><published>2015-03-18T06:58:00+00:00</published><updated>2015-03-18T06:58:00+00:00</updated><id>/blog/2015/03/18/unity-text-effects</id><content type="html" xml:base="/blog/2015/03/18/unity-text-effects.html">&lt;p&gt;&lt;strong&gt;Gradient&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

[AddComponentMenu(&quot;UI/Effects/Gradient&quot;)]
public class Gradient : BaseVertexEffect {
    [SerializeField]
    private Color32 topColor = Color.white;
    [SerializeField]
    private Color32 bottomColor = Color.black;

    public override void ModifyVertices(List vertexList) {
        if (!IsActive()) {
            return;
        }

        int count = vertexList.Count;
        float bottomY = vertexList[0].position.y;
        float topY = vertexList[0].position.y;

        for (int i = 1; i &amp;lt; count; i++) {             float y = vertexList[i].position.y;             if (y &amp;gt; topY) {
                topY = y;
            }
            else if (y &amp;lt; bottomY) {
                bottomY = y;
            }
        }

        float uiElementHeight = topY - bottomY;

        for (int i = 0; i &amp;lt; count; i++) {
            UIVertex uiVertex = vertexList[i];
            uiVertex.color = Color32.Lerp(bottomColor, topColor, (uiVertex.position.y - bottomY) / uiElementHeight);
            vertexList[i] = uiVertex;
        }
    }
}&lt;/pre&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="GUI" /><category term="Text" /><category term="Unity" /><category term="Unity3D" /><summary type="html">Gradient using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; [AddComponentMenu(&quot;UI/Effects/Gradient&quot;)] public class Gradient : BaseVertexEffect { [SerializeField] private Color32 topColor = Color.white; [SerializeField] private Color32 bottomColor = Color.black; public override void ModifyVertices(List vertexList) { if (!IsActive()) { return; } int count = vertexList.Count; float bottomY = vertexList[0].position.y; float topY = vertexList[0].position.y; for (int i = 1; i &amp;lt; count; i++) { float y = vertexList[i].position.y; if (y &amp;gt; topY) { topY = y; } else if (y &amp;lt; bottomY) { bottomY = y; } } float uiElementHeight = topY - bottomY; for (int i = 0; i &amp;lt; count; i++) { UIVertex uiVertex = vertexList[i]; uiVertex.color = Color32.Lerp(bottomColor, topColor, (uiVertex.position.y - bottomY) / uiElementHeight); vertexList[i] = uiVertex; } } }</summary></entry><entry><title type="html">Overview of iOS Crash Reporting Tools</title><link href="/blog/2014/07/21/overview-of-ios-crash-reporting-tools.html" rel="alternate" type="text/html" title="Overview of iOS Crash Reporting Tools" /><published>2014-07-21T11:34:00+00:00</published><updated>2014-07-21T11:34:00+00:00</updated><id>/blog/2014/07/21/overview-of-ios-crash-reporting-tools</id><content type="html" xml:base="/blog/2014/07/21/overview-of-ios-crash-reporting-tools.html">&lt;p&gt;Believe it or not, developers are not perfect, and every once in a while you might have a (gasp!) bug in your app.&lt;/p&gt;

&lt;p&gt;You will try your best to ship your apps with no bugs in them, but more often than not you realise afterwards that a bug has slipped through the net. Sometimes such bugs result in crashes, which no user likes to encounter.
&lt;!--more--&gt;
Never fear though, for there are many excellent iOS crash reporting tools to help you out! They can detect crashes and log details so you can investigate later, and even send you an email summary of when crashes occur and how often.&lt;/p&gt;

&lt;p&gt;However, you may wonder which of these tools is the best? Read on to find out!&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;You’ve spent months working on your newest application, you’ve spent weeks beta testing it, you’ve published it on the App Store, and the positive reviews are starting to roll in. Everything looks great!&lt;/p&gt;

&lt;p&gt;But then one user posts a one-star review noting the worst thing possible — the app crashed. Yikes! Nothing is worse than hearing about crashes from the user community.&lt;/p&gt;

&lt;p&gt;Although it’s nearly impossible to test for every possible scenario, you can make the triage and troubleshooting portion of your job a little easier by using crash reporting tools in your apps. In fact, they are possibly the most important tool you can make use of to improve your application. Even if you test it thoroughly before the submission to the store, chances are you may have forgotten to check a particular sequence or scenario which leads to a crash.&lt;/p&gt;

&lt;p&gt;From a user perspective, a crash is more than just an annoyance. When the application just stops working without any feedback, this often results in lost data and unhappy users. Installing a crash reporting system in your app allows you to obtain all the information you need to be able to fix these annoyances.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/05/Fotolia_35452506_XS.jpg&quot;&gt;&lt;img src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/05/Fotolia_35452506_XS.jpg&quot; alt=&quot;App crashes are like death and taxes - they happen. But it's up to you how to handle them!&quot; width=&quot;322&quot; height=&quot;373&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;App crashes are like death and taxes – they happen. But it’s up to you how to handle them!&lt;/p&gt;

&lt;p&gt;When it comes to troubleshooting, the more information that is collected — such as the OS version, the device type, and the application version — the more details you have to track down the source of the bug and fix it. That way, you can turn each crash incident into an opportunity to improve your app!&lt;/p&gt;

&lt;p&gt;In this two-part series, you’ll get an overview of the different options of crash reporting tools available to developers. You’ll learn about various free and commercial tools, as well as the various pros and cons of each tool. By the end of the first part, you’ll be able to make an informed choice as to the best tool for your needs. In the second part, you will learn how to get started with each crash reporting service and tie it into your app.&lt;/p&gt;

&lt;p&gt;Sound good? Then let’s take a behind-the-scenes look at what goes into a crash reporting tool.&lt;/p&gt;
&lt;h2&gt;Crash Reporting Basics&lt;/h2&gt;
&lt;p&gt;A crash reporting tool is actually a combination of two components: a reporting library and a server-side collector. You can think of these items in terms of a restaurant: the reporting library is the kitchen and the collector is the waiter. The developer — i.e. you — plays the part the customer.&lt;/p&gt;

&lt;p&gt;You can have the best organized kitchen in the world, but you’ll never eat anything if there isn’t a waiter to bring food to the table. Likewise, your waiter can provide excellent service, but if the food is not well prepared, your experience will not be a good one!&lt;/p&gt;

&lt;p&gt;As a great restaurant should include a great kitchen and great waiters, a crash reporting tool should include a great reporting library and a great server-side collector. The role of the reporting library is to prepare the details about a crash. The role of the server-side component is to collect the crash data and present it in a meaningful way.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Symbolication&lt;/h2&gt;
&lt;p&gt;In iOS crash reporting, there’s one more thing that’s crucial to understanding your crash reports – symbolication. But what is symbolication? You can think of symbolication as the chef in the kitchen, who transforms raw ingredients like bananas, ice cream and rum into something meaningful and tasty such as &lt;a href=&quot;http://en.wikipedia.org/wiki/Bananas_Foster&quot;&gt;Bananas Foster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A crash report contains a stack trace of every thread that was running when the application terminated. This trace is similar to the traces you see in the debugger when paused, although a debugger’s trace will include both instance names and methods (often referred as symbols). In comparison, when an app is built for release, usually these symbol names are stripped from the binary. This means that crash reports coming into your crash reporter service contain strange looking hexadecimal addresses instead of symbol names.&lt;/p&gt;

&lt;p&gt;You will no doubt have at least one crash log on your device, so to see what these hexadecimal addresses look like, find such a log like this:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Connect your device to your Mac.&lt;/li&gt;
	&lt;li&gt;Start Xcode and open the Organizer (CMD+Shift+2).&lt;/li&gt;
	&lt;li&gt;Find your device on the left and select the “Device Logs” item as shown in the screenshot below:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img title=&quot;Device in organizer&quot; src=&quot;http://cdn1.raywenderlich.com/wp-content/uploads/2013/02/phone.png&quot; alt=&quot;A device as listed in the organizer of Xcode&quot; width=&quot;194&quot; height=&quot;118&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A list of crash reports will show up, unless your device is brand new and you haven’t used it much. Select one application, and have a look to the right. You should see text displayed similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;An example of Crash&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/hexaCrash.png&quot; alt=&quot;An example of Crash&quot; width=&quot;699&quot; height=&quot;256&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Notice how there are some symbols, for example &lt;code&gt;-[__NSArrayI objectAtIndex:]&lt;/code&gt;, and some hexadecimal addresses, for example &lt;code&gt;0x000db142 0xb1000 + 172354&lt;/code&gt;. This is what is known as a partially symbolicated crash log. The top of the stack trace is where the crash occurred, and going down the list indicates which other methods were called to get to where the crash occurred.&lt;/p&gt;

&lt;p&gt;The reason that it’s a “partly” symbolicated log is because Xcode is able to symbolicate just the system components like UIKit and CoreFoundation (lines 6 to 18 in the screenshot above). But crashes aren’t usually generated by system libraries, instead they have their roots in the code you write. For example, line 2 indicates that the code path that lead to the crash went through the &lt;code&gt;objectAtIndex:&lt;/code&gt; method on &lt;code&gt;NSArray&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, take a look at line 3 in the screenshot above. What’s the meaning of this?&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p336691&quot;&gt;
&lt;td id=&quot;p33669code1&quot;&gt;
&lt;pre&gt;0x000db142 0xb1000 + 172354&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;This is an example of an un-symbolicated portion of the log. All this tells us is that the crash occurred at the instruction at memory address &lt;code&gt;0x000db142&lt;/code&gt; which is equal to &lt;code&gt;0xb1000 + 172354&lt;/code&gt;. The reason for it telling us that seemingly obscure bit of math is that &lt;code&gt;0xb1000&lt;/code&gt; is the start address of the main portion of the app. The 172354 is the important bit as it is the offset into the app.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; All these numbers relate to byte offsets within the memory addressable to the CPU. When an application is run, the binary is loaded into memory and read instruction by instruction, by the CPU. The offset where the program is loaded, in this case 0xb1000, is randomised as a minimal form of security against exploits.

&lt;/div&gt;
&lt;p&gt;But, that’s not terribly useful for debugging purposes. A raw crash report contains what you need to understand the &lt;i&gt;cause&lt;/i&gt; of the crash, but you can’t fully comprehend it unless you dive a little deeper and match it up with the source code through a process known as “symbolication”. This is the process of turning these raw numbers into symbols and even pinpointing the exact line of code.&lt;/p&gt;
&lt;h2&gt;The Symbolication Process&lt;/h2&gt;
&lt;p&gt;In the case of iOS, this requires two things. The first is the exact application binary that caused the crash. The second is the dSYM file created during the compilation of the binary.&lt;/p&gt;

&lt;p&gt;How do you get a hold of these two items? Well, if you are using the “Build and Archive” command in Xcode then you are already storing them! In Xcode, you can see these in the “Organizer” window. To see for yourself, open Xcode then go to &lt;em&gt;Window\Organizer&lt;/em&gt;. Then select the “Archives” tab where you will see a list of your apps on the left and on the right you’ll see a list of the archives for the selected app.&lt;/p&gt;

&lt;p&gt;However, if you haven’t been using “Build and Archive”, or don’t have any applications to choose from, simply create a dummy app to see where Xcode stores these files.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; If you’re familiar with the Organizer, feel free to skip this section and move on to the section titled “Find that dSYM!”

&lt;/div&gt;
&lt;p&gt;To create a dummy app. Start up Xcode, go to &lt;em&gt;File\New\New Project&lt;/em&gt;, choose the &lt;em&gt;iOS\Application\Empty Application&lt;/em&gt; template, and click &lt;em&gt;Next&lt;/em&gt;, as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1/create_empty_application&quot; rel=&quot;attachment wp-att-36867&quot;&gt;&lt;img src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/04/create_empty_application-700x471.png&quot; alt=&quot;create_empty_application&quot; width=&quot;650&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page, feel free to put in any name for the application. The details don’t matter because this is just a throwaway app to create an archive with a binary and dSYM in it. Click &lt;em&gt;Next&lt;/em&gt;, then choose a location to save your project. When Xcode presents the workspace, select &lt;em&gt;iOS Device&lt;/em&gt; as the compilation target, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Scheme in Xcode&quot; src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/02/scheme.png&quot; alt=&quot;Scheme in Xcode&quot; width=&quot;436&quot; height=&quot;73&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally, select &lt;em&gt;Product\Archive&lt;/em&gt;, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Archive action in Xcode&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/archive.png&quot; alt=&quot;Archive action in Xcode&quot; width=&quot;212&quot; height=&quot;179&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This will generate an archive for your application, which can then be found in the Organizer.&lt;/p&gt;
&lt;h2&gt;Find that dSYM!&lt;/h2&gt;
&lt;p&gt;Go to the Organizer, by selecting &lt;em&gt;Window\Organizer&lt;/em&gt;, select the &lt;em&gt;Archives&lt;/em&gt; tab and then find either an archive for one of your own apps or the test app you made if you followed the previous section. In case you’re struggling to find the archive, here’s a screenshot of what the Organizer looks like:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Archived app in the Organizer&quot; src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/02/archivedApps-e1361874512820.png&quot; alt=&quot;Archived app in the Organizer&quot; width=&quot;693&quot; height=&quot;455&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Right-click on your choice of archive, and choose &lt;em&gt;Show in finder&lt;/em&gt;. This will open up a Finder window with a file whose extension is .xcarchive. This is not a file, per se, but rather a folder.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt;Just like archives, lots of “files” on Mac OS X and iOS are actually folders. Examples include applications (.app), iPhoto libraries (.photolibrary) and code frameworks (.framework).

&lt;/div&gt;
&lt;p&gt;To inspect the archive, first open a new terminal window. To open a terminal window, open a new Finder window, navigate to your Applications folder, then navigate to the Utilities subfolder, and finally run the Terminal application. Or you could simply type “terminal” into Spotlight and press return when it’s found Terminal. :]&lt;/p&gt;

&lt;p&gt;Once Terminal is open, drag the .xcarchive file into the terminal window and you’ll see the complete path to the folder. Press &lt;em&gt;Control-A&lt;/em&gt; to jump to the start of the path and type: &lt;code&gt;&quot;cd &quot;&lt;/code&gt; (note that there is a space after the “cd”).&lt;/p&gt;

&lt;p&gt;Next, press enter. You’ll notice that the terminal prompt (the bit before where the cursor sits) now has the name of the .xcarchive before the dollar sign.&lt;/p&gt;

&lt;p&gt;Next, type the following command: &lt;code&gt;&quot;find . -type d&quot;&lt;/code&gt;. You should see some output similar to the following:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p336692&quot;&gt;
&lt;td id=&quot;p33669code2&quot;&gt;
&lt;pre&gt;$ find . -type d
.
./Products
./Products/Applications
./Products/Applications/breezi.app
./Products/Applications/breezi.app/_CodeSignature
./Products/Applications/breezi.app/en.lproj
./dSYMs
./dSYMs/breezi.app.dSYM
./dSYMs/breezi.app.dSYM/Contents
./dSYMs/breezi.app.dSYM/Contents/Resources
./dSYMs/breezi.app.dSYM/Contents/Resources/DWARF&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The command you just typed is using the “find” program, which does exactly what it sounds like – it finds things! The “.” means “search in the current directory” and the “-type d” means “find things that are directories”. In the resulting list of files, notice the &lt;code&gt;.dSYM&lt;/code&gt; directory, along with the &lt;code&gt;.app&lt;/code&gt; directory. Those are what you’ll need to finish the symbolication process and tie the crash report to your code.&lt;/p&gt;

&lt;p&gt;It’s the .dSYM that’s really the most important bit. Recall from earlier that a release build has all the symbols stripped from the binary, which is why crash reports from release builds contain only hexadecimal addresses. The .dSYM contains all the information required to convert back into symbols through the process of symbolication.&lt;/p&gt;

&lt;p&gt;Xcode has a method of symbolication built into it and it’ll generally do a good job of making sure any crash log you ever see has been symbolicated. It uses spotlight to find the appropriate .dSYM file for the crash log in question, but sometimes it can’t find it (maybe because you didn’t have it on your computer at the time as the build was made on a build server). In these cases, you can force Xcode to re-symbolicate once you have the .dSYM copied to your computer. To force Xcode to re-symbolicate a crash you can do so in the Organizer. Go to the Organizer again, select the “Devices” tab and then select “Device Logs” on the left. Then select the crash log that needs re-symbolicating and press the button at the bottom of the screen:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://cdn3.raywenderlich.com/wp-content/uploads/2013/05/Resymbolicate-button.png&quot; alt=&quot;Resymbolicate button&quot; width=&quot;257&quot; height=&quot;82&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s not just Xcode that uses the .dSYM to symbolicate crash reports. It’s also required by crash reporting tools. As you’ll see throughout the reviews of the various crash reporting tools, the way you get this file to the crash reporting tool is different, but it’s always required for symbolication. There’s no point having crash logs if you can’t read them after all!&lt;/p&gt;
&lt;h2&gt;Making a Case for Crash Reporting&lt;/h2&gt;
&lt;p&gt;It’s an &lt;em&gt;incredibly&lt;/em&gt; clunky process to collect crash logs manually and since a user will probably not have Xcode installed, they would have to use iTunes to access the crash logs. While not a technique I recommend asking your users to do, here’s how you’d do it in the absence of crash reporting tools:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Connect the device to the Mac, synch it via iTunes and navigate to the logs folder, which is different on each operating system:
&lt;ul&gt;
	&lt;li&gt;&lt;em&gt;OS X&lt;/em&gt;: ~/Library/Logs/CrashReporter/MobileDevice/(your iPhone’s name)/(your app name)&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Windows XP&lt;/em&gt;: C:\Documents and Settings\Application Data\Apple computer\Logs\CrashReporter\(your iPhone’s name)\(your app name)&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Windows Vista&lt;/em&gt;: C:\Users\AppData\Roaming\Apple computer\Logs\CrashReporter\MobileDevice\(your iPhone’s name)\(your app name)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
	&lt;li&gt;Look for files with the extension of “.crash”.&lt;/li&gt;
	&lt;li&gt;Ask the user to send those files to you.&lt;/li&gt;
	&lt;li&gt;Once you have the files, open the Organizer in Xcode and select “Device Logs” on the top left, as shown below:&lt;img title=&quot;Device Logs item in Organizer&quot; src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/02/devLog.png&quot; alt=&quot;Device Logs item in Organizer&quot; width=&quot;188&quot; height=&quot;96&quot; /&gt;&lt;/li&gt;
	&lt;li&gt;At the bottom select import to add the log to Xcode and, if necessary because Xcode hasn’t noticed the log yet, trigger a re-symbolication as explained earlier.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Phew! Can you imagine any user volunteering to do this for you?&lt;/p&gt;

&lt;p&gt;This solution is pretty tedious, to say the least, and it requires the active involvement of your users. That might be OK when you’re in the beta testing phase, and your testers expect a bit of weirdness with the app. However, once you’re in production don’t expect that your users will be happy to go through all that work to give you a crash report!&lt;/p&gt;

&lt;p&gt;Interestingly enough, Apple helps out and collects crash reports for applications that are published through the App Store. However, it only happens for users who opted to automatically send diagnostic data to Apple. If you have an app available through, the App Store login to &lt;a href=&quot;http://itunesconnect.apple.com&quot;&gt;iTunes Connect&lt;/a&gt;, select “Manage Your Applications”, choose one of your applications and the click the blue button “View details”, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Application as Shown on the App Store&quot; src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/02/appStore.png&quot; alt=&quot;Application as Shown on the App Store&quot; width=&quot;479&quot; height=&quot;272&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This will open a detailed view of your app’s properties. At the top right there is a button “Crash Reports”, as shown in the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Locating Crash Reports on the App Store&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/appStore2-700x201.png&quot; alt=&quot;Locating Crash Reports on the App Store&quot; width=&quot;700&quot; height=&quot;201&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This section will contain the crash logs collected by Apple. In my experience, these aren’t collected as frequently as with other crash reporting tools, probably because not all that many people have automatic sending of diagnostic reports turned on. Plus, they still require some manual work from developers, like importing and symbolicating.&lt;/p&gt;

&lt;p&gt;The smart developer, who would rather spend time writing code than manually retrieving and symbolicating crash dumps, would prefer a process like this:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Release an application (either in beta or in production) which is able to capture and store crash logs.&lt;/li&gt;
	&lt;li&gt;The application periodically (or even immediately!) sends crash reports to a server which collects and organizes them for you.&lt;/li&gt;
	&lt;li&gt;The developer logs in to the server, finds the crash reports already symbolicated, and can easily find the root cause of the issue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s not a far-fetched dream — tools like this do exist in the real world! However, there are quite a few to choose from. Some are free and open source, while others are commercial products. Of the many available, I’ve reviewed some of the most prominent offerings:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Crashlytics&lt;/li&gt;
	&lt;li&gt;Crittercism&lt;/li&gt;
	&lt;li&gt;Bugsense&lt;/li&gt;
	&lt;li&gt;TestFlight&lt;/li&gt;
	&lt;li&gt;HockeyApp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Have a read through the reviews to see which one provides the particular solutions you’re looking for in your app. I have my own favorites, but I’d love to hear your thoughts on your personal choices in the comments section!&lt;/p&gt;
&lt;h2&gt;Crashlytics&lt;/h2&gt;
&lt;p&gt;Recently bought by Twitter, Crashlytics is pretty famous in the iOS community. It’s used by well-known companies such as Path and Yammer. It is a full-stack service, meaning that the framework provides both client-side and server-side parts. At the moment, Crashlytics supports only iOS, although the website does indicate that Android support is coming soon.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crashlytics Setup and Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you have logged into the Crashlytics site, you’ll be prompted to download a Mac application. This application will guide you through the process of configuring your iOS application to work with the Crashlytics service. The application works like a wizard. It first asks you to pick an Xcode project on your disk. Next, it will install the Crashlytics framework, and finally, it will add a step to the building phase of your project.&lt;/p&gt;

&lt;p&gt;Any crashes captured will appear on the Crashlytics back-end. Just login, select the application you’re interested in, and you’ll see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;The Dashboard on Crashlytics&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/crashlytics-700x376.png&quot; alt=&quot;The Dashboard on Crashlytics&quot; width=&quot;700&quot; height=&quot;376&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The data is definitely well-organized. At a glance, you can see the issues reported, number of crashes, and the number of users affected. On the right, a graph shows the distribution of crashes over time. Each crash is classified by application version to avoid any confusion.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crashlytics Reports&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Crashlytics displays the list of crashes at the bottom, already symbolicated, and you can immediately see the line generating the crash. In the example above, you can quickly see that line 343 in SMEngine.m was where the crash occurred.&lt;/p&gt;

&lt;p&gt;If you click on a crash entry, further details are displayed, as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;A Log Report on Crashlytics&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/crashlytics2-e1361875571818.png&quot; alt=&quot;A Log Report on Crashlytics&quot; width=&quot;700&quot; height=&quot;990&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At the top of the report, there are contextual details about the average environment where the crash occurred, like the free space on the device, which is handy if you are caching data on disk; RAM, which is handy if you are caching data in memory; and whether the device is jailbroken, along with other juicy details.&lt;/p&gt;

&lt;p&gt;At the bottom, you’ll see a stack trace with the sequence of calls that occurred right before the app crashed. As noted, it is very likely some of your code is causing the crash, so you should look for the names of your classes and methods. In this example, the crash is in &lt;code&gt;getHourlyForecast&lt;/code&gt;, which is called by &lt;code&gt;parseResultForConnection&lt;/code&gt;, which in turn is called by &lt;code&gt;connectionDidFinishLoading&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you have fixed the bug and deployed the new version of the app, you can mark the issue as closed, using the control shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Close an Issue on Crashlytics&quot; src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/02/crashlytics3-700x148.png&quot; alt=&quot;Close an Issue on Crashlytics&quot; width=&quot;700&quot; height=&quot;148&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once an issue is closed, it won’t appear again in the list of crashes that require your attention.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crashlytics 3rd Party Integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can integrate Crashlytics with third party bug trackers and project management tools, including the following:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://campfirenow.com&quot;&gt;Campfire&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.atlassian.com/software/jira/overview&quot;&gt;JIRA&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.pivotaltracker.com&quot;&gt;Pivotal Tracker&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.redmine.org&quot;&gt;Redmine&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.pagerduty.com&quot;&gt;PagerDuty&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Simply enter your credentials for the above services, and Crashlytics servers will forward crash log data to the appropriate service. Cool! :]&lt;/p&gt;

&lt;p&gt;For those developers who love to craft their own solution, you can even set up a web hook. This is a custom URL where Crashlytics will send you data in JSON format, and you’re then free to manipulate the data as you like.&lt;/p&gt;

&lt;p&gt;The logging functions that Crashlytics provides are worth mentioning as well. Once you have configured your project for Crashlytics, you can quickly set up a macro to log events, actions and most importantly, the values of variables, which can be helpful while hunting for the source of a crash.&lt;/p&gt;

&lt;p&gt;This is the equivalent of using &lt;code&gt;NSLog&lt;/code&gt; statements, except instead of logging to the console, the lines are sent to Crashlytics. How many times have you wished you could see the console output as your customers use your app in the real world? That’s a reality now! :]&lt;/p&gt;

&lt;p&gt;You can also set up notifications via email to receive messages about crashes either as they are received, or as a daily email digest.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crashlytics Usage Tiers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Previously, Crashlytics had two usage tiers: a free tier, as well as a paid enterprise tier. However, the enterprise tier is now free as well. Thanks Twitter!&lt;/p&gt;

&lt;p&gt;To go back to the restaurant analogy, Crashlytics is a good restaurant with a proficient kitchen (symbolication). The food is good and the wait staff are attentive (browsability and usability of logs on the server). Unfortunately, there is not much variety in the menu (just iOS). There is also a wait list, much like a very good restaurant, which means you may have to wait to be seated. However the wait time at the moment doesn’t appear to be very long and you are often seated within minutes.&lt;/p&gt;
&lt;h2&gt;Crittercism&lt;/h2&gt;
&lt;p&gt;Crittercism is another full-stack tool to keep track of your crash logs. It has been adopted by companies such as Netflix, Eventbrite and Linkedin. It provides support for iOS, as well as Android, HTML5 and Windows 8 (which is in beta at the moment).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crittercism Setup and Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Setup is pretty simple. You create an account, create an application on the server, download an SDK, add it to your project, and initialize it. Then you’re ready to rock!&lt;/p&gt;

&lt;p&gt;The screenshot below shows the dashboard view of Crittercism, along with a symbolicated crash log:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;An example of Crittercism log&quot; src=&quot;http://cdn4.raywenderlich.com/wp-content/uploads/2013/02/crittercism1-e1361877259704.png&quot; alt=&quot;An example of Crittercism log&quot; width=&quot;700&quot; height=&quot;677&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With Crittercism, not every detail is available at first glance. For example, to view the application version or contextual data, you have to navigate through the tabbed menu in the middle of the page.&lt;/p&gt;

&lt;p&gt;The “breadcrumbs” feature of Crittercism allows you to place log statements throughout your code to get contextual information about what happened before the crash, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Log statements on crittercism&quot; src=&quot;http://cdn1.raywenderlich.com/wp-content/uploads/2013/02/crittercism2-700x359.png&quot; alt=&quot;Log statements on crittercism&quot; width=&quot;700&quot; height=&quot;359&quot; /&gt;&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note&lt;/em&gt;: Breadcrumbs is a paid enterprise feature of Crittercism, but it’s included in the trial period if you want to get a feel for how it works.

&lt;/div&gt;
&lt;p&gt;Like other tools, you can mark a crash log as “known” or “solved”. Finally, the SDK includes the possibility to schedule a “Rate My App” popup via the backend, with the ability to customize when the popup appears, as well as the message displayed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crittercism Incident Mapping&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Another interesting feature of Crittercism is the map that shows you where your logs were recorded, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Distribution on log on a map on Crittercism&quot; src=&quot;http://cdn3.raywenderlich.com/wp-content/uploads/2013/02/crittercism3-700x334.png&quot; alt=&quot;Distribution on log on a map on Crittercism&quot; width=&quot;700&quot; height=&quot;334&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Personally, I have some concern about this, since the user is never asked for permission to calculate a user’s current position. You can use your own judgment call on this.&lt;/p&gt;

&lt;p&gt;You can receive email notifications when a crash log is uploaded to the server, and you can set up alarms to receive SMS or email messages when crash counts pass a given threshold.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crittercism 3rd Party Integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As far as third party integration goes, you can hook Crittercism up to &lt;a href=&quot;http://www.helpshift.com/&quot;&gt;HelpShift&lt;/a&gt; or &lt;a href=&quot;https://www.uservoice.com&quot;&gt;Uservoice&lt;/a&gt;. Both are customer support help-desk applications.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Crittercism Usage Tiers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are three pricing tiers for Crittercism at the moment, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1/pricing-tiers&quot; rel=&quot;attachment wp-att-40130&quot;&gt;&lt;img src=&quot;http://cdn5.raywenderlich.com/wp-content/uploads/2013/05/pricing-tiers-700x258.png&quot; alt=&quot;Pricing Tiers&quot; width=&quot;700&quot; height=&quot;258&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you sign up, you are offered a 30-day free trial which includes enterprise level features like breadcrumbs and phone support.&lt;/p&gt;

&lt;p&gt;The price of the full-tier enterprise solution isn’t published — you’ll have to contact Crittercism directly to get enterprise pricing after the trial expires.&lt;/p&gt;

&lt;p&gt;Crittercism is an interesting restaurant. The menu is very rich (offering iOS, Android, HTML5 and Windows 8 in beta) but the wait staff is not really friendly (requiring manual upload of dSYM files, and the usability of the website isn’t the best).&lt;/p&gt;
&lt;h2&gt;Bugsense&lt;/h2&gt;
&lt;p&gt;Bugsense is another full-stack service, used by big companies such as Samsung, Intel and Groupon. It supports iOS, Android, Windows 8, Windows Phone and HTML5.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bugsense Setup and Dashboard&lt;/em&gt;
The setup is pretty similar to other platforms: create an account, create an application, download an SDK, include it in your project, and set the API key.&lt;/p&gt;

&lt;p&gt;The Bugsense dashboard for an application looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1/bug_sense_dashboard&quot; rel=&quot;attachment wp-att-40139&quot;&gt;&lt;img src=&quot;http://cdn4.raywenderlich.com/wp-content/uploads/2013/05/bug_sense_dashboard-700x377.png&quot; alt=&quot;bug_sense_dashboard&quot; width=&quot;700&quot; height=&quot;377&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click one of the logs, you are presented with a detailed view, like so:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1/crash-detail&quot; rel=&quot;attachment wp-att-40142&quot;&gt;&lt;img src=&quot;http://cdn3.raywenderlich.com/wp-content/uploads/2013/05/crash-detail-700x385.png&quot; alt=&quot;Crash Detail Page&quot; width=&quot;700&quot; height=&quot;385&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the class that caused the crash — in this case, &lt;code&gt;NSInvalidArgumentException&lt;/code&gt; — the function generating it, and the corresponding line of code (SMViewController.m:26).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bugsense Crash Reports&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The log presentation is chock-full of detail, and the user interface allows the ability to customize which attributes are displayed. It also displays the number of times a crash has occurred. As usual, you can mark issues as resolved.&lt;/p&gt;

&lt;p&gt;One of the downsides of Bugsense is that you have to manually upload the dSYM file of your build to allow symbolication on the server-side. You &lt;i&gt;can&lt;/i&gt; configure the app to run symbolication right on the device, although the documentation discourages it because you don’t actually get full information such as code line numbers.&lt;/p&gt;

&lt;p&gt;To keep track of the application’s context during run-time, you can drop breadcrumbs into your code at key points and they will be uploaded to the server, together with data about the crash. You can also use events to perform this functionality, but they still look pretty similar to breadcrumbs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bugsense Push Notifications&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An interesting feature is “Fix notifications”. This feature allows you to send your users a push notification to indicate that an upgrade is available for the app. This is pretty handy when you have resolved a bug and released a new version, but some of your users is still running an old version.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bugsense 3rd Party Integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The backend of Bugsense can be integrated with &lt;a href=&quot;http://www.atlassian.com/software/jira/overview&quot;&gt;JIRA&lt;/a&gt; to push data about crash logs. Like other tools, you will receive email notifications when the platform receives a crash log.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Bugsense Usage Tiers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are four pricing tiers for Bugsense as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1/pricing_details&quot; rel=&quot;attachment wp-att-40144&quot;&gt;&lt;img src=&quot;http://cdn1.raywenderlich.com/wp-content/uploads/2013/05/pricing_details-510x500.png&quot; alt=&quot;Pricing Details&quot; width=&quot;510&quot; height=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bugsense is a very nice restaurant. The menu is really varied (iOS, Android, Windows 8, Windows Phone and HTML5) and well organized. The wait staff is not always impeccable, but given the other great features of the restaurant, it’s something you can manage to live with.&lt;/p&gt;
&lt;h2&gt;TestFlight&lt;/h2&gt;
&lt;p&gt;TestFlight was born as a tool to manage the distribution of beta releases. Over time the developers added many more features, like action logging and crash reporting. It’s like a restaurant that has a bar or a club to entertain the client before or after dinner.&lt;/p&gt;

&lt;p&gt;TestFlight has been adopted by companies such as Adobe, Instagram and tumblr to manage over-the-air deployment, tracking and crash reporting. Both iOS and Android are supported at present.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TestFlight Setup and Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Getting started with TestFlight is similar to other crash reporting services: create an account, create an application, download and integrate the SDK, and finally, setup an application key.&lt;/p&gt;

&lt;p&gt;On the server-side, you have to produce a build of your app, upload the &lt;code&gt;.ipa&lt;/code&gt; file of your distribution, at which point you can ping your testers to download the new build. This can either be done directly through the TestFlight website, or through the &lt;a href=&quot;http://testflightapp.com/desktop/&quot;&gt;supplied Mac app&lt;/a&gt;. This app also determines when Xcode’s archive process has been completed, then prompts you to upload the new build.&lt;/p&gt;

&lt;p&gt;The dashboard on the server-side looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Dashboard on testflight&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/testflight-700x439.png&quot; alt=&quot;Dashboard on testflight&quot; width=&quot;700&quot; height=&quot;439&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A handy menu on the left allows you to browse different aspects of your build like sessions, checkpoints crashes (similar to breadcrumbs), and user feedback.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TestFlight User Feedback&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TestFlight provides a feedback view in your application to collect feedback from your testers. The feedback view appears to users like this:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Feedback view on TestFlight&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/testflight-feedback.png&quot; alt=&quot;Feedback view on TestFlight&quot; width=&quot;320&quot; height=&quot;480&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TestFlight Crash Reports&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A crash log on the web server looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;A Crash log on TestFlight&quot; src=&quot;http://cdn4.raywenderlich.com/wp-content/uploads/2013/02/tf-700x332.png&quot; alt=&quot;A Crash log on TestFlight&quot; width=&quot;700&quot; height=&quot;332&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As noted previously, you can log when a user reaches a critical point in your app, such as opening a particular view. This provides you with contextual information which is useful when hunting for the root cause of a crash.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TestFlight 3rd Party Integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At the moment there is no integration with third party tools, although you can hook up with the upload API to automate the upload phase.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TestFlight Usage Tiers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The TestFlight service is free at the moment; there’s no separate free or paid tiers. As for the restaurant analogy — this restaurant/bar/club is an interesting place to hang out if you want to spend the whole night in one spot. The menu is pretty limited (iOS and Android only), and the neighborhood is bit desolate (there’s no integration with third-party tools).&lt;/p&gt;
&lt;h2&gt;HockeyApp&lt;/h2&gt;
&lt;p&gt;HockeyApp is pretty well known in the indie developer world — probably because it’s made by indie developers! :]. It supports iOS, Android, MacOS, and Windows Phone. Like TestFlight, it’s a restaurant with perks, not just food. In fact, besides crash reporting and just like TestFlight, it includes distribution management.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HockeyApp Setup and Dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve created an account you are invited to create an app and download the corresponding SDK. The setup is pretty standard: import a framework, set up the API key and you are ready to go. Alternatively, you can opt for the more complicated approach of including the full source to the framework instead. This is good to know that option exists, incase you find a bug in the framework and desperately need the fix before the HockeyApp team fix it themselves. That’s a rare occurrence, but it’s good to know that the option exists!&lt;/p&gt;

&lt;p&gt;Here’s what the HockeyApp dashboard looks like:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Dashboard on hockeyapp&quot; src=&quot;http://cdn1.raywenderlich.com/wp-content/uploads/2013/02/hockeyapp-e1361882100809.png&quot; alt=&quot;Dashboard on hockeyapp&quot; width=&quot;699&quot; height=&quot;543&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The dashboard gives you an overview of your builds with a handy menu at the top to show different sections of the dashboard along with a nice graph of various stats at the bottom.&lt;/p&gt;

&lt;p&gt;The companion desktop application detects when the archive procedure has finished and prompts you to upload the new build, including the dSYM. Once you’ve manually uploaded the dSYM file to the server, you can select the Crashes tab, pick a log and you’ll see a symbolicated crash, as below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;A crash log on hockeyapp&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/02/hockeyapp2-700x440.png&quot; alt=&quot;A crash log on hockeyapp&quot; width=&quot;700&quot; height=&quot;440&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The HockeyApp back-end provides the ability to perform advanced searches through your logs, using criteria like “show all of the crashes that happened on iOS6 but not on an iPad”.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HockeyApp Crash Uploads&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A unique feature is the ability to upload crashes you receive via email from your users. This is made possible by the fact that the HockeyApp crash log format is similar to the format adopted by Apple. You can also log events, much like using breadcrumbs, and attach them to a crash log via a simple API.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HockeyApp 3rd Party Integration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The integration with third-party tools is quite rich. HockeyApp integrates with the following tools and services, among others:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.atlassian.com/software/jira/overview&quot;&gt;JIRA&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.mantisbt.org&quot;&gt;Mantis&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://lighthouseapp.com&quot;&gt;Lighthouse&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.redmine.org&quot;&gt;Redmine&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://trac.edgewall.org&quot;&gt;Trac&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://basecamp.com&quot;&gt;Basecamp&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.pivotaltracker.com&quot;&gt;Pivotal tracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like all other crash reporting tools, you can set the frequency and type of email notifications that you receive from HockeyApp.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HockeyApp Usage Tiers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;HockeyApp boasts four pricing tiers at the moment, with discounts available if you sign up for yearly subscriptions:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Pricing tiers of hockeyapp&quot; src=&quot;http://cdn4.raywenderlich.com/wp-content/uploads/2013/02/hockeyapp3-700x353.png&quot; alt=&quot;Pricing tiers of hockeyapp&quot; width=&quot;700&quot; height=&quot;353&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s worth mentioning that HockeyApp is the hosted version of the open source solution Quincykit. If you’re keen on setting up your own back-end to collect logs, check out &lt;a href=&quot;http://quincykit.net&quot;&gt;http://quincykit.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;HockeyApp is a nice restaurant and the organization and variety of the menu is great (supporting iOS, Android, MacOS, and Windows Phone). The neighborhood (3rd party integration) is lovely and well-populated.&lt;/p&gt;
&lt;h2&gt;Summary and Comparison Chart&lt;/h2&gt;
&lt;p&gt;I’ve provided a table below comparing the features of the tools reviewed above:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;Comparison table of crash reporting tools&quot; src=&quot;http://cdn2.raywenderlich.com/wp-content/uploads/2013/05/table.png&quot; alt=&quot;Comparison table of crash reporting tools&quot; width=&quot;700&quot; height=&quot;600&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;The Bottom Line&lt;/h2&gt;
&lt;p&gt;If most of your development work is on iOS, the best crash reporting tool in my opinion is Crashlytics. It’s free, with tons of features and a very usable back-end. Moreover, the reporting process is fully automated, as you get logs on the server with no need to manually upload dSYM files for each release. One drawback is that it doesn’t manage distribution of your app.&lt;/p&gt;

&lt;p&gt;If you’re looking for a complete service cross-platform choice, I’d suggest Bugsense, because of the usability of its dashboard. Be aware, though — the cheaper tiers only retain data for a short period, ranging from 7 to 30 days.&lt;/p&gt;

&lt;p&gt;However, all of the services reviewed above are still valid alternatives, depending on the features and usability you’re looking for.&lt;/p&gt;

&lt;p&gt;In the second part of this article, I will show how to get started with each of these services, integrate it with your app, and give you a tour of the various crash logs and other features.&lt;/p&gt;

&lt;p&gt;Thanks for joining me for the second part of this two-part series on crash reporting services!&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1&quot;&gt;first part&lt;/a&gt; introduced you to the architecture of crash reporting services, including storage, symbolication, and server-side management. As well, I provided a basic overview and comparison chart of the most popular crash reporting services today.&lt;/p&gt;

&lt;p&gt;In this second part, I’ll take you through the steps to get started with each service covered in the last article: Crashlytics, Crittercism, Bugsense, TestFlight and HockeyApp.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Throughout this article, you will work with a very simple iPhone application that just contains a table view, as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/App.png&quot;&gt;&lt;img title=&quot;The sample App for this Tutorial&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/App.png&quot; alt=&quot;The sample App for this Tutorial&quot; width=&quot;320&quot; height=&quot;480&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This particular app has been constructed to generate crash events. That’s bad news in the real word — but for our purposes, it will serve perfectly!&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;The app provides a pizza menu with two functions:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Swipe to delete a pizza from the menu.&lt;/li&gt;
	&lt;li&gt;Scroll to the bottom to load more pizzas.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The code for our pizza application can be found &lt;a title=&quot;Starter project for this tutorial&quot; href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashy-starter.zip&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Download the project, then build and run in the simulator. To cause a crash, swipe to delete a row, or simply scroll to the bottom of the list.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note&lt;/em&gt;: The sample app is designed to run on iOS6 and above. If you are testing on an pre iOS6 device, you will need to disable AutoLayout.

To do so, open &lt;em&gt;SMViewController.xib&lt;/em&gt;, select the File Inspector, then uncheck “Use AutoLayout”, as shown below:

&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/disable_autolayout.png&quot;&gt;&lt;img title=&quot;disable_autolayout.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/disable_autolayout.png&quot; alt=&quot;Disable AutoLayout&quot; width=&quot;600&quot; height=&quot;266&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;

Now when your app crashes, you’ll really mean for it to happen! :]

&lt;/div&gt;
&lt;p&gt;Although I know it’s killing you not to fix the bugs, don’t! You want the application to crash so that a reporting tool can help you to identify the source of the problem.&lt;/p&gt;

&lt;p&gt;Here are a list of the crash reporting services that I’ll cover in this article:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Crashlytics (&lt;a href=&quot;https://www.crashlytics.com/&quot; target=&quot;_blank&quot;&gt;www.crashlytics.com)&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Crittercism (&lt;a href=&quot;https://www.crittercism.com/&quot; target=&quot;_blank&quot;&gt;www.crittercism.com&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;Bugsense (&lt;a href=&quot;http://www.bugsense.com/&quot; target=&quot;_blank&quot;&gt;www.bugsense.com&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;TestFlight (&lt;a href=&quot;https://www.testflightapp.com/&quot; target=&quot;_blank&quot;&gt;www.testflightapp.com&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;HockeyApp (&lt;a href=&quot;http://www.hockeyapp.net/&quot; target=&quot;_blank&quot;&gt;www.hockeyapp.net&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep in mind that there is a waiting period for Crashlytics where it may take several days for Crashlytics to contact you. However, if it does take a long time then no fear because Crashlytics has generously provided RayWenderlich.com with &lt;a href=&quot;http://try.crashlytics.com/?s=magnolia&quot;&gt;a special link&lt;/a&gt; that will help you jump the queue. Try that and see if you get in the VIP door a little faster! :]&lt;/p&gt;

&lt;p&gt;Before continuing, make sure you create an account with each of the crash reporting frameworks and download each of the crash reporting SDKs.&lt;/p&gt;

&lt;p&gt;Since some crash reporting tools require a unique identifier to function correctly, make sure to change the sample project’s bundle identifier to something of your own design, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/change_bundle_identifier.png&quot;&gt;&lt;img title=&quot;change_bundle_identifier.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/change_bundle_identifier.png&quot; alt=&quot;Change bundle identifier&quot; width=&quot;600&quot; height=&quot;358&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have a set a custom bundle identifier, make sure to make a backup copy of the project. Each framework has their own installation instructions so it is best to use a “vanilla” project with each demo.&lt;/p&gt;

&lt;p&gt;In order to test out these crash reporting frameworks, you will need to run the app on a real device. This requires an Apple developer account as well as a provisioned device. If you are a new to iOS development, you can learn how to create a developer account, as well as provisioning your own device in &lt;a href=&quot;http://www.raywenderlich.com/8003/how-to-submit-your-app-to-apple-from-no-account-to-app-store-part-1&quot;&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note&lt;/em&gt;: During this tutorial, do not run the application on the device through Xcode. Xcode will intercept the crash, then open the lldb debugger shell as usual. You won’t get any crash reports if Xcode intercepts the crash event!

To make all the examples below work, you have to build and run the application, then click the stop button on Xcode. This way you will have the latest version installed on the the device.

Once that is done, you can launch the app on the device itself, and then crash it all you want!

All the crashes on your iOS device will be caught and sent to the server component of the service that you have integrated into the app. Crash reports are usually sent to the server the next time you start the app, so the steps to follow to generate a crash report on the server are as follows:
&lt;ol&gt;
	&lt;li&gt;Build and run on Xcode.&lt;/li&gt;
	&lt;li&gt;Press the stop button.&lt;/li&gt;
	&lt;li&gt;Run the app on your iOS device.&lt;/li&gt;
	&lt;li&gt;Make the app crash.&lt;/li&gt;
	&lt;li&gt;Run the app again.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;As well, make sure the device has connectivity via wi-fi or 3G so that the crash reports can be sent. Now, let’s get crashing! :]&lt;/p&gt;
&lt;h1&gt;Crashlytics&lt;/h1&gt;
&lt;p&gt;Recently bought by Twitter, Crashlytics is pretty famous in the iOS community. It’s used by well-known companies such as Path and Yammer. It is a full-stack service, meaning that the framework provides both client-side and server-side parts.&lt;/p&gt;

&lt;p&gt;At the moment, Crashlytics supports only iOS, although the website does indicate that Android support is coming soon.&lt;/p&gt;
&lt;h2&gt;Crashlytics — Configuring the Project&lt;/h2&gt;
&lt;p&gt;Once you have logged into Crashlytics, you will be prompted to download a Mac OSX application that will help you to set up your first project.&lt;/p&gt;

&lt;p&gt;To save you time, here’s the &lt;a href=&quot;https://www.crashlytics.com/download/mac&quot;&gt;direct link&lt;/a&gt; to the Mac application. When running, the application appears in the menu bar on the top right as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics1.png&quot;&gt;&lt;img title=&quot;Crashlytics in the menu bar&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics1.png&quot; alt=&quot;Crashlytics in the menu bar&quot; width=&quot;188&quot; height=&quot;23&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the icon and enter your credentials when prompted. If this is your first time using Crashlytics, you should see an empty list of applications and your account name at the bottom, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics2.png&quot;&gt;&lt;img title=&quot;Crashlytics after login&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics2.png&quot; alt=&quot;Crashlytics after login&quot; width=&quot;333&quot; height=&quot;553&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;em&gt;New App&lt;/em&gt; button on the right. This will show you the list of recent projects opened in Xcode, like so:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics3.png&quot;&gt;&lt;img title=&quot;Recent Projects in Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics3.png&quot; alt=&quot;Recent Projects in Crashlytics&quot; width=&quot;353&quot; height=&quot;561&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your app is not listed, you’ll need to perform the following steps:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Click the “Other” button at the bottom to open a Finder window.&lt;/li&gt;
	&lt;li&gt;Select the .xcodeproj file and click “Open”.&lt;/li&gt;
	&lt;li&gt;Select your project from the list in Crashlytics and hit “Next”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This will automatically open the project in Xcode and ask you to add a script to the Build Phase. From this point onward, the Crashlytics app will walk you through each step of installation process. Follow the instructions, and when you’re done, continue with the next step of the tutorial.&lt;/p&gt;
&lt;h2&gt;Crashlytics — Running the App&lt;/h2&gt;
&lt;p&gt;Now that you’ve completed the setup portion, you are now ready to try out Crashlytics. Run the application on your iOS device, swipe on a cell and tap &lt;em&gt;Delete&lt;/em&gt;. The application will crash, as expected.&lt;/p&gt;

&lt;p&gt;Restart the app and wait about a minute to make sure the crash report has propagated to the Crashlytics server. Then take a peek at the website to see if the crash report is live. You should also receive an email notification about a new crash in your application.&lt;/p&gt;

&lt;p&gt;The new issue on the back-end should look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics10.png&quot;&gt;&lt;img title=&quot;First issue on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics10-700x282.png&quot; alt=&quot;First issue on Crashlytics&quot; width=&quot;700&quot; height=&quot;282&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance, you will see the guilty line of code (&lt;em&gt;SMViewController.m&lt;/em&gt; at line 80), the number of users affected, and the number of occurrences of this crash. Launch the app and make it crash again. You’ll now see the number of crashes of the same issue increase.&lt;/p&gt;

&lt;p&gt;Click the row of that issue and you’ll see the extended report, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics11.png&quot;&gt;&lt;img title=&quot;Detailed view of the report on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics11-e1362321061492.png&quot; alt=&quot;Detailed view of the report on Crashlytics&quot; width=&quot;699&quot; height=&quot;1001&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the crash log, it’s clear that you need to counterbalance the deletion of a cell with the deletion of its corresponding element in the array populating the table view. To fix this bug, open &lt;em&gt;SMViewController.m&lt;/em&gt; and modify &lt;code&gt;tableView:commitEditingStyle:forRowAtIndexPath:&lt;/code&gt; to look like the following code:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340501&quot;&gt;
&lt;td id=&quot;p34050code1&quot;&gt;
&lt;pre&gt;- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/&quot;&gt;NSIndexPath&lt;/a&gt; *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [pizzaOrder removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;After you are done, build and run, push the app to your device, and test the application again. Once you have determined that you have fixed the issue, return to the Crashlytics crash report then set the issue status as closed, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/crashlytics_close_bug.png&quot;&gt;&lt;img title=&quot;crashlytics_close_bug.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/crashlytics_close_bug.png&quot; alt=&quot;Crashlytics close bug&quot; width=&quot;600&quot; height=&quot;510&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On to the second bug!&lt;/p&gt;

&lt;p&gt;Crashlytics allows you to add log statements to your code to help track down bugs. Open &lt;em&gt;SMViewController.m&lt;/em&gt; and add the following import statement at the top of the file:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340502&quot;&gt;
&lt;td id=&quot;p34050code2&quot;&gt;
&lt;pre&gt;#import &amp;lt;Crashlytics/Crashlytics.h&amp;gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Next, modify &lt;code&gt;tableView:willDisplayCell:forRowAtIndexPath:&lt;/code&gt; in &lt;em&gt;SMViewController.m&lt;/em&gt; as follows.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/&quot;&gt;NSIndexPath&lt;/a&gt; *)indexPath { if (indexPath.row == pizzaOrder.count-1) { CLS_LOG(@&quot;posting notification&quot;); [Crashlytics setIntValue:pizzaOrder.count forKey:@&quot;numberOfPizzas&quot;]; [[&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/&quot;&gt;NSNotificationCenter&lt;/a&gt; defaultCenter] postNotificationName:LOAD_MORE_NOTIFICATION object:nil]; } }&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;CLS_LOG&lt;/em&gt; is a macro provided by the Crashlytics framework that enables remote logging. In debug builds, it acts just like NSLog, passing strings to the console. For release builds, the log is sent along with the crash reports and optimized to be as fast as possible. In fact, Crashlytics’ own &lt;a href=&quot;http://support.crashlytics.com/knowledgebase/articles/92519-how-do-i-use-logging-&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt; boasts a 10x improvement over using regular &lt;code&gt;NSLog()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;The method also uses custom logging which allows you to store information in key-value form. Think of this as a type of NSDictionary on the web server: you set the values of keys in the code, and you can read the values on the server when hunting down a bug.&lt;/p&gt;

&lt;p&gt;In this case you are using &lt;em&gt;setIntValue:forKey&lt;/em&gt;, but there are other methods available to store objects, floats and booleans as well. You can find more details about custom logging in the &lt;a href=&quot;http://support.crashlytics.com/knowledgebase/articles/92520-how-do-i-use-custom-keys-&quot; target=&quot;_blank&quot;&gt;Crashlytics knowledgebase&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run the application in Xcode and then stop it. Run it again on your device, scroll to the bottom, allow the app to crash, and restart the application after the crash.&lt;/p&gt;

&lt;p&gt;The new issue will look like this on the Crashlytics dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics12.png&quot;&gt;&lt;img title=&quot;Second issue on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics12-700x282.png&quot; alt=&quot;Second issue on Crashlytics&quot; width=&quot;700&quot; height=&quot;282&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is clear that this is due to the lack of the method definition &lt;code&gt;loadMore&lt;/code&gt; in &lt;em&gt;SMWebEngine.m&lt;/em&gt;. If you were to add the definition for this method, and the crash would disappear. Although that’s not the point of this tutorial, so don’t worry about actually implementing it!&lt;/p&gt;

&lt;p&gt;Notice that the detailed view of each issue provides information about the device like iOS version, and free space on disk and in memory. All these details might help you when you are hunting down the root cause of a crash.&lt;/p&gt;

&lt;p&gt;Click on “More details…” in the middle of the page. This will show even more details like the type of device, orientation, plus the keys and log statements that you have spread throughout your code, as shown in the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics14.png&quot;&gt;&lt;img title=&quot;Logs and keys on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics14-700x399.png&quot; alt=&quot;Logs and keys on Crashlytics&quot; width=&quot;700&quot; height=&quot;399&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way you can have a path of breadcrumbs that can help to hunt down the cause of the crash. It is important to note that logs and keys are sent to the server as attachments to a crash log, so you won’t see them if there aren’t any crashes.&lt;/p&gt;

&lt;p&gt;Crashlytics, unlike other services like TestFlight, is not a generic remote logging application, but rather a true crash reporting application. So even though your app may be full of logging statements, if the app never crashes, you’ll never see any of your logs!&lt;/p&gt;

&lt;p&gt;One useful feature for beta testing is the ability to ask your users to provide some data to identify themselves. Crashlytics provides three ways to attach this data to a crash log:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340504&quot;&gt;
&lt;td id=&quot;p34050code4&quot;&gt;
&lt;pre&gt;[Crashlytics setUserIdentifier:@”123456”];
[Crashlytics setUserName:@”cesarerocchi”];
[Crashlytics setUserEmail:@”cesare@mailaddress.com”];&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;This way, you can identify who is experiencing a crash, and contact that tester to get more details.&lt;/p&gt;

&lt;p&gt;Finally, you can configure some logging features on the back-end. Each application has a settings section that you can open by clicking the gear button, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics15.png&quot;&gt;&lt;img title=&quot;App settings on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics15.png&quot; alt=&quot;App settings on Crashlytics&quot; width=&quot;365&quot; height=&quot;106&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here you can disable reports from a specific version, or request a user for permission before sending data about the report. When you enable it (and you should!), you can customize the message as shown in the screenshot below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics16.png&quot;&gt;&lt;img title=&quot;Privacy on Crashlytics&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crashlytics16.png&quot; alt=&quot;Privacy on Crashlytics&quot; width=&quot;628&quot; height=&quot;563&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Crashlytics — Summing It Up&lt;/h2&gt;
&lt;p&gt;This concludes the guided tour of Crashlytics. If you are just targeting the iOS market, I highly recommend that you consider Crashlytics. The service is very quick to upload crashes, there are no hassles with dSYM files, and the back-end service is quite intuitive.&lt;/p&gt;
&lt;h1&gt;Crittercism&lt;/h1&gt;
&lt;p&gt;Crittercism is another full-stack tool to keep track of your crash logs. It has been adopted by companies such as Netflix, Eventbrite and Linkedin. It provides support iOS, as well as Android, HTML5 and Windows 8 (which is in beta at the moment).&lt;/p&gt;
&lt;h2&gt;Crittercism — Configuring the Project&lt;/h2&gt;
&lt;p&gt;To get started with Crittercism, there are a few steps to follow:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Register an application.&lt;/li&gt;
	&lt;li&gt;Download and import the SDK.&lt;/li&gt;
	&lt;li&gt;Configure the Xcode project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After you login to Crittercism, assuming it’s your first time, you will end up at the following screen:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit1.png&quot;&gt;&lt;img title=&quot;First step in Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit1-700x429.png&quot; alt=&quot;First step in Crittercism&quot; width=&quot;700&quot; height=&quot;429&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tap the big blue button on the top left. You’ll be presented with the following screen:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit2.png&quot;&gt;&lt;img title=&quot;Creating the first app on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit2-e1362321800174.png&quot; alt=&quot;Creating the first app on Crittercism&quot; width=&quot;700&quot; height=&quot;615&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will ask you to assign a name; for this project, put “crashy”. Then select the &lt;em&gt;iOS&lt;/em&gt; platform, and don’t bother to invite any collaborators. You’re just testing the application, so choose &lt;em&gt;No&lt;/em&gt; for the question “In App Store?”. When you’re done, click the big blue &lt;em&gt;Register App&lt;/em&gt; button at the bottom.&lt;/p&gt;

&lt;p&gt;Next you will be prompted to download the most recent version of the SDK (which is 3.5.1 at the time of this writing). Here’s the link to the &lt;a href=&quot;https://www.crittercism.com/downloads/ios&quot;&gt;download page&lt;/a&gt;, just in case.&lt;/p&gt;

&lt;p&gt;Open a new, unfixed copy of the crashy-starter project in Xcode, unzip the Crittercism SDK, drag and drop the folder named &lt;em&gt;CrittercismSDK&lt;/em&gt; onto the root of our project, and make sure “Copy items into destination group’s folder” is checked.&lt;/p&gt;

&lt;p&gt;Go to the &lt;em&gt;Build Phases&lt;/em&gt; tab of the target. Open the &lt;em&gt;Link Binary with Libraries&lt;/em&gt; section by clicking on it. You’ll notice that &lt;code&gt;libCrittercism&lt;/code&gt; is already linked. Add the SystemConfiguration framework and QuartzCore by clicking on the plus sign, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit3.png&quot;&gt;&lt;img title=&quot;Framework linking in Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit3-700x258.png&quot; alt=&quot;Framework linking in Crittercism&quot; width=&quot;700&quot; height=&quot;258&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you need to find the App ID. This is the ID for Crittercism and has nothing to do with Apple’s application ID. Head to &lt;a href=&quot;https://www.crittercism.com/developers&quot;&gt;the Crittercism dashboard&lt;/a&gt;, select your application from the list, and click the settings tab on the left. You will see a section that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit4.png&quot;&gt;&lt;img title=&quot;API key on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit4.png&quot; alt=&quot;API key on Crittercism&quot; width=&quot;519&quot; height=&quot;287&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This provides a portion of code to copy and paste into &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; of &lt;em&gt;SMAppDelegate.m&lt;/em&gt;. First, add the following import statement at the top of &lt;em&gt;SMAppDelegate.m&lt;/em&gt;:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340505&quot;&gt;
&lt;td id=&quot;p34050code5&quot;&gt;
&lt;pre&gt;#import &quot;Crittercism.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now add the Crittercism supplied code to the top of &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; :&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340506&quot;&gt;
&lt;td id=&quot;p34050code6&quot;&gt;
&lt;pre&gt;[Crittercism enableWithAppID:@&quot;&amp;lt;YOUR_CRITTERCISM_APP_ID&amp;gt;&quot;];&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;And that’s it, your project is configured to work with Crittercism!&lt;/p&gt;
&lt;h2&gt;Crittercism — Uploading the dSYM&lt;/h2&gt;
&lt;p&gt;Unlike Crashlytics, you have to manually upload your dSYM file to the server. To find the dSYM file, follow the instructions in the &lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1#symbolication&quot;&gt;first part&lt;/a&gt; of the tutorial in the Symbolication section.&lt;/p&gt;

&lt;p&gt;Recall from the &lt;a href=&quot;http://www.raywenderlich.com/33669/overview-of-ios-crash-reporting-tools-part-1&quot;&gt;first part&lt;/a&gt; of this tutorial that a dSYM is actually a directory. For this reason, you’ll need to zip your dSYM folder first before uploading it to Crittercism. To upload your zipped dSYM file use the tab &lt;em&gt;Upload dSYMs&lt;/em&gt; in the “Settings” section of your app, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit5.png&quot;&gt;&lt;img title=&quot;Upload dSYM on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit5-700x320.png&quot; alt=&quot;Upload dSYM on Crittercism&quot; width=&quot;700&quot; height=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note&lt;/em&gt;: If you are still having problems uploading your dSYM file, check out this &lt;a href=&quot;http://www.youtube.com/watch?v=i2hv35MisgM&quot; target=&quot;_blank&quot;&gt;YouTube video&lt;/a&gt; that screencasts the process, step by step.

&lt;/div&gt;
&lt;p&gt;Once you have uploaded the dSYM folder, run your application, either on your simulator or on your device, and the console should print the following message:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340507&quot;&gt;
&lt;td id=&quot;p34050code7&quot;&gt;
&lt;pre&gt;Crittercism successfully initialized.&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Let’s start by tracking down the first bug.&lt;/p&gt;
&lt;h2&gt;Crittercism — Running the App&lt;/h2&gt;
&lt;p&gt;As you did before, run the application without Xcode, attempt to delete a cell, and restart the application. Now visit the &lt;em&gt;Crash Reports&lt;/em&gt; section of your application on the back-end. You should see your crash report as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit6.png&quot;&gt;&lt;img title=&quot;Dashboard on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit6-700x368.png&quot; alt=&quot;Dashboard on Crittercism&quot; width=&quot;700&quot; height=&quot;368&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dashboard will show an overview of your application with a big graph at the top and the list of issues at the bottom. Just pretend you don’t know the what the source of the bug is, and click the issue in the list to inspect it. You’ll see a detailed report similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit7.png&quot;&gt;&lt;img title=&quot;A crash report on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit7-e1362322220278.png&quot; alt=&quot;A crash report on Crittercism&quot; width=&quot;699&quot; height=&quot;830&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “Reason” column should lead you to think there is an issue with the table view. You might see more details, but the log is not yet symbolicated, so you don’t know which file and line of code is responsible for the crash.&lt;/p&gt;

&lt;p&gt;You’ll need to add the log to a queue to be symbolicated, by clicking the &lt;em&gt;Add to Symbolication Queue&lt;/em&gt; link highlighted in the screenshot above. Once it has been symbolicated, you will see the decoded thread of the crash, which indicated an issue on line 80 in file &lt;em&gt;SMViewController.m&lt;/em&gt;, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit8.png&quot;&gt;&lt;img title=&quot;Decoded log on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit8-700x229.png&quot; alt=&quot;Decoded log on Crittercism&quot; width=&quot;700&quot; height=&quot;229&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One shortcoming of Crittercism is that I couldn’t find a way to queue crash logs automatically as they get uploaded. If you’ve found a way around this, please let me know in the comments!&lt;/p&gt;

&lt;p&gt;If you select the “Users” tab, you’ll notice that Crittercism automatically generates IDs to identify users, as shown in the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit9.png&quot;&gt;&lt;img title=&quot;User ids on Crittercism&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/crit9-700x168.png&quot; alt=&quot;User ids on Crittercism&quot; width=&quot;700&quot; height=&quot;168&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now mark the issue as resolved using the drop down menu on the left side of the graph as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;resolve_crittercism_bug.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/resolve_crittercism_bug.png&quot; alt=&quot;Resolve Crittercism Bug&quot; width=&quot;600&quot; height=&quot;267&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to collect more specific data about users, the Crittercism class also provides the following methods:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340508&quot;&gt;
&lt;td id=&quot;p34050code8&quot;&gt;
&lt;pre&gt;[Crittercism setUsername:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)username];

[Crittercism setEmail:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)email];

[Crittercism setGender:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)gender];

[Crittercism setAge:(int)age];

[Crittercism setValue:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)value forKey:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)key];&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;An interesting feature is the ability to log handled exceptions on the server as follows:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p340509&quot;&gt;
&lt;td id=&quot;p34050code9&quot;&gt;
&lt;pre&gt;@try {
        [&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSException_Class/&quot;&gt;NSException&lt;/a&gt; raise:NSInvalidArgumentException
                    format:@&quot;Argument must be a string&quot;];
        } @catch (&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSException_Class/&quot;&gt;NSException&lt;/a&gt; *exc) {
            [Crittercism logHandledException:exc]
        }&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Crittercism also offers breadcrumb logging for enterprise accounts and an assortment of other features.&lt;/p&gt;
&lt;h2&gt;Crittercism — Summing it Up&lt;/h2&gt;
&lt;p&gt;This concludes the how-to portion on Crittercism. The Crittercism framework also supports Android, Windows 8 and HTML5.&lt;/p&gt;

&lt;p&gt;I’ve found that the user interface for the back-end is a bit complex, requiring a few extra clicks to get to the required information. It’s a bit clumsy to manually upload the dSYM for each build, but overall, it’s a solid framework.&lt;/p&gt;
&lt;h2&gt;Okay Class — Time for a Break!&lt;/h2&gt;
&lt;p&gt;Okay, it’s time for a short break from your crash reporting studies. Answer the following tricky question.&lt;/p&gt;
&lt;div&gt;
&lt;table border=&quot;0&quot; align=&quot;center&quot; bgcolor=&quot;FFFFFF&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Solution Inside: Is divide-by-zero an operation that causes a crash on ARM?&lt;/th&gt;
&lt;th&gt;&lt;a id=&quot;spoilerDiv65eb8001_action&quot; href=&quot;http://www.raywenderlich.com/34050/overview-of-ios-crash-reporting-tools-part-2?utm_source=tuicool&quot;&gt;Show&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;2&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;
&lt;table border=&quot;0&quot; frame=&quot;box&quot; align=&quot;center&quot; bgcolor=&quot;FFFFFF&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;td colspan=&quot;2&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;2&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;BugSense&lt;/h1&gt;
&lt;p&gt;Bugsense is another full-stack service, used by big companies such as Samsung, Intel and Groupon. It supports iOS, Android, Windows 8, Windows Phone and HTML5.&lt;/p&gt;
&lt;h2&gt;Bugsense — Configuring the Project&lt;/h2&gt;
&lt;p&gt;Once you are logged in to Bugsense, you will end up in the dashboard which should be empty if this is your first time using Bugsense. Click the “Add New Project” button, enter a name for your project, select &lt;em&gt;iOS&lt;/em&gt; for Technology, &lt;em&gt;Testing&lt;/em&gt; for the stage, then click &lt;em&gt;Submit&lt;/em&gt;, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense1.png&quot;&gt;&lt;img title=&quot;Creating an Application on Bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense1.png&quot; alt=&quot;Creating an Application on Bugsense&quot; width=&quot;538&quot; height=&quot;470&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The website will then show a dashboard with no data, since you have not yet integrated the framework. When you first visit your project page, a welcoming lightbox will greet you which will contain your API key, as so:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;bugsense_api_key.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/bugsense_api_key1.png&quot; alt=&quot;Bugsense API Key&quot; width=&quot;600&quot; height=&quot;468&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Copy down this key as you’ll be needing it later!&lt;/p&gt;

&lt;p&gt;Unfortunately there is no direct link to download the SDK, so you will have to click the &lt;em&gt;Docs&lt;/em&gt; item in the top right bar of the dashboard. This will open the generic documentation page, from which you have to select iOS. You will end up at &lt;a href=&quot;https://www.bugsense.com/docs/ios&quot; target=&quot;_blank&quot;&gt;this link&lt;/a&gt;. This page should contain a URL to download the iOS SDK (which is version 3.2 at the time of this writing).&lt;/p&gt;

&lt;p&gt;Unzip the downloaded ZIP file and it will create a folder named &lt;em&gt;BugSense-iOS.framework&lt;/em&gt;. Next, open a new copy of the starter project and go to the &lt;em&gt;Build Phases&lt;/em&gt; tab. As previously, expand &lt;em&gt;Link Binary With Libraries&lt;/em&gt;, click the &lt;em&gt;+&lt;/em&gt; button, choose &lt;em&gt;Add other&lt;/em&gt;, and select the unzipped &lt;em&gt;BugSense-iOS.framework&lt;/em&gt; folder. Also add &lt;em&gt;SystemConfiguration&lt;/em&gt; and &lt;em&gt;libz.dylib&lt;/em&gt;. The link section should now look like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense3.png&quot;&gt;&lt;img title=&quot;Framework linking on bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense3.png&quot; alt=&quot;Framework linking on bugsense&quot; width=&quot;645&quot; height=&quot;166&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Switch to the &lt;em&gt;Build Settings&lt;/em&gt; tab and make sure that “Strip Debug Symbols During Copy” and “Strip Linked Product” are both set to YES as shown here:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense4.png&quot;&gt;&lt;img title=&quot;More project settings on bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense4-700x362.png&quot; alt=&quot;More project settings on bugsense&quot; width=&quot;700&quot; height=&quot;362&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you just need to insert a few hooks to integrate your app with Bugsense. Insert the following statement at the top of &lt;em&gt;SMAppDelegate.h&lt;/em&gt;:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405010&quot;&gt;
&lt;td id=&quot;p34050code10&quot;&gt;
&lt;pre&gt;#import &amp;lt;BugSense-iOS/BugSenseController.h&amp;gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Next, open &lt;em&gt;SMAppDelegate.m&lt;/em&gt; and add the following code to the beginning of &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt;, using the API key you copied down earlier:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405011&quot;&gt;
&lt;td id=&quot;p34050code11&quot;&gt;
&lt;pre&gt;[BugSenseController sharedControllerWithBugSenseAPIKey:@&quot;YOUR API KEY&quot;];&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now you’re ready to test Bugsense with your first bug!&lt;/p&gt;
&lt;h2&gt;Bugsense — Running the App&lt;/h2&gt;
&lt;p&gt;Just like in the previous sections, build and archive your project, install the application (.ipa file) on the device, and zip the dSYM file for later upload. Once the application is installed, run it, make it crash by deleting a row, and restart the app. Head over to the dashboard of Bugsense and open your project. You’ll see the following report:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;bugsense_user_interface.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/bugsense_user_interface.png&quot; alt=&quot;Bugsense User Interface&quot; width=&quot;600&quot; height=&quot;251&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A nice graph shows the number of crashes per day. Underneath is the list of error logs — just one in this case. You can immediately see the generic reason behind this issue; click the issue to open the detailed report as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;symbolicate.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/symbolicate.png&quot; alt=&quot;Symbolicate&quot; width=&quot;600&quot; height=&quot;151&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The stack trace as shown is complete, but has not yet been symbolicated. Click the &lt;em&gt;Symbolicate&lt;/em&gt; link on the top right. In this case, it will fail to symbolicate the trace because the dSYM is not yet on the server, and the button will turn green with a label of “Upload Symbols”.&lt;/p&gt;

&lt;p&gt;Click the &lt;em&gt;Upload Symbols&lt;/em&gt; button, and you will be redirected to the settings section of the application, where you can upload your zipped dSYM folder, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;upload_dsym_files.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/upload_dsym_files.png&quot; alt=&quot;Upload dSYM Files&quot; width=&quot;600&quot; height=&quot;283&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you have uploaded your dSYM files, head back to the detailed view of the error log and again click the &lt;em&gt;Symbolicate&lt;/em&gt; link. This time you will see that line 7 is decoded, clearly showing the line responsible for the crash:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense8.png&quot;&gt;&lt;img title=&quot;Symbolicated crash on bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense8.png&quot; alt=&quot;Symbolicated crash on bugsense&quot; width=&quot;685&quot; height=&quot;62&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below the crash log, there is also a handy table showing lots of details about the crash. This table can be customized using the gear icon on the right to show many more pieces of data like locale, milliseconds elapsed from the start of the application, data about the mobile carrier and WiFi status, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense9.png&quot;&gt;&lt;img title=&quot;Report details on bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense9-700x95.png&quot; alt=&quot;Report details on bugsense&quot; width=&quot;700&quot; height=&quot;95&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally, I am a bit concerned about the UDID, which has been deprecated since iOS5.&lt;/p&gt;

&lt;p&gt;To mark a crash as resolved, return to the application dashboard, select the crash in question, then press the button labeled &lt;em&gt;Resolve Selected&lt;/em&gt;, as below:&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;resolve_open_bug.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/resolve_open_bug.png&quot; alt=&quot;Resolve Open Bug&quot; width=&quot;600&quot; height=&quot;91&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also provide the number of the release that includes the fix by clicking the &lt;em&gt;Settings&lt;/em&gt; link in the navigation bar then selecting &lt;em&gt;Fix Notifications&lt;/em&gt;. You can even personalize the message to be sent via push notification like so:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense11.png&quot;&gt;&lt;img title=&quot;Setting notifications on bugsense&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/bugsense11-700x471.png&quot; alt=&quot;Setting notifications on bugsense&quot; width=&quot;700&quot; height=&quot;471&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bugsense also supports breadcrumbs, but they are only accessible in higher paid plans.&lt;/p&gt;
&lt;h2&gt;Bugsense — Summing it Up&lt;/h2&gt;
&lt;p&gt;This ends the how-to portion of Bugsense. The platform is quite feature-rich and supports Windows8, Windows Phone and HTML5.&lt;/p&gt;

&lt;p&gt;A desktop application to automate the upload of dSYMs would definitely be appreciated, as well as the automatic symbolication of crash logs when they are received on the server.&lt;/p&gt;
&lt;h1&gt;TestFlight&lt;/h1&gt;
&lt;p&gt;TestFlight was born as a tool to manage the distribution of beta releases. Over time the developers added many more features, like action logging and crash reporting.&lt;/p&gt;

&lt;p&gt;TestFlight has been adopted by companies such as Adobe, Instagram and tumblr to manage over-the-air deployment, tracking and crash reporting. iOS is the only platform supported at the moment.&lt;/p&gt;
&lt;h2&gt;TestFlight — Configuring the Project&lt;/h2&gt;
&lt;p&gt;As mentioned in the previous part of this article, TestFlight is a bit more than a crash reporting system, for it also allows you to recruit testers and distribute your test builds. Once you have logged in, head to &lt;a href=&quot;https://testflightapp.com/sdk/download/&quot;&gt;the SDK download page&lt;/a&gt; to download the SDK. The current version at the time of this writing is 1.2.4.&lt;/p&gt;

&lt;p&gt;Open a new copy of the starter project, unzip the TestFlight SDK, drag the entire folder onto the root of the project, and make sure “Copy items into destination folder (if needed)” is checked.&lt;/p&gt;

&lt;p&gt;In the target settings, select the &lt;em&gt;Build Phases&lt;/em&gt; tab and then open the &lt;em&gt;Link Binary With Libraries&lt;/em&gt; section. &lt;em&gt;libTestFlight.a&lt;/em&gt; should appear in the list of linked libraries. Also click the “+” button and add the &lt;em&gt;libz.dylib&lt;/em&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf2.png&quot;&gt;&lt;img title=&quot;Framework linking in TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf2.png&quot; alt=&quot;Framework linking in TestFlight&quot; width=&quot;641&quot; height=&quot;151&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;em&gt;Build Settings&lt;/em&gt; tab and select “NO” for the following flags:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Deployment Postprocessing&lt;/li&gt;
	&lt;li&gt;Strip Debug Symbols During Copy&lt;/li&gt;
	&lt;li&gt;Strip Linked Product&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compile the application using Cmd+B to check if everything is ok. Next you’ll need a particular token to make your app work with TestFlight. To get this token, go to your &lt;a href=&quot;https://testflightapp.com/dashboard/team/edit/&quot; target=&quot;_blank&quot;&gt;TestFlight team dashboard&lt;/a&gt; and copy the team token displayed. That’s the key you’ll need to initialize the framework in the code.&lt;/p&gt;

&lt;p&gt;Open &lt;em&gt;SMAppDelegate.m&lt;/em&gt; and add the following import:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405012&quot;&gt;
&lt;td id=&quot;p34050code12&quot;&gt;
&lt;pre&gt;#import &quot;TestFlight.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Insert the following code at the beginning of &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; in &lt;em&gt;SMAppDelegate.m&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;[TestFlight takeOff:@&quot;YOUR TOKEN HERE&quot;]; ///&amp;lt; Replacing &quot;YOUR TOKEN HERE&quot; with the token you found on your team dashboard&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Now you’re ready to test the application against the first bug. I suggest you download and install the companion desktop application before doing this, which you can download from &lt;a href=&quot;https://testflightapp.com/desktop/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. This app is smart enough to detect when you have archived an application and offers to upload both the .ipa file and the dSYM. This will save you some pointing and clicking on the TestFlight website.&lt;/p&gt;

&lt;p&gt;If you’ve never set up a binary for adhoc distribution, TestFlight has provided some &lt;a href=&quot;http://help.testflightapp.com/customer/portal/articles/829857&quot; target=&quot;_blank&quot;&gt;detailed instructions&lt;/a&gt; to get you started.&lt;/p&gt;

&lt;p&gt;If you use the desktop application (which I highly suggest you do!), follow the wizard’s instructions and at the end you’ll be prompted to copy out the share URL. That’s the URL of the new build you’ve just submitted — you can send this link to your testers.&lt;/p&gt;

&lt;p&gt;To install the app to your device, simply open that URL on your device. It’s that easy! :]. Run the app, crash it by swiping and hitting “Delete”, and restart the app. Now check to see if your crash report shows up on the web back-end by heading to &lt;a href=&quot;https://testflightapp.com/dashboard/builds/&quot;&gt;the TestFlight dashboard&lt;/a&gt;. If all went as expected, you should see something similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf3.png&quot;&gt;&lt;img title=&quot;A build on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf3-700x65.png&quot; alt=&quot;A build on TestFlight&quot; width=&quot;700&quot; height=&quot;65&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; Crash logs sometimes take a while before showing up on the web interface. TestFlight support indicated that these issues will be resolved in the next release of the SDK.

&lt;/div&gt;
&lt;p&gt;This view already provides some interesting data about your build – the number of crashes, feedback and total installations. In your case, you should see one install and one crash. Click the &lt;em&gt;Latest build&lt;/em&gt; link to get the detailed report, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf4.png&quot;&gt;&lt;img title=&quot;Activity view on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf4-700x316.png&quot; alt=&quot;Activity view on TestFlight&quot; width=&quot;700&quot; height=&quot;316&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A menu on the left gives you an overview of the data related to the build, such as the number of session and the total number of crashes. In the middle section you can see the list of users and on the far right, a summary of the recent activity performed on the application by users.&lt;/p&gt;

&lt;p&gt;Tap on the &lt;em&gt;Crashes&lt;/em&gt; tab on the left and expand the log using the little arrow on the right. You’ll see the complete stack trace, as below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf.png&quot;&gt;&lt;img title=&quot;A detailed crash log on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf-700x332.png&quot; alt=&quot;A detailed crash log on TestFlight&quot; width=&quot;700&quot; height=&quot;332&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another interesting feature in TestFlight is the ability to add checkpoints. Checkpoints are much like breadcrumbs or events – you spread some log statements in key places in your code to get a better idea of what happened before a crash.&lt;/p&gt;

&lt;p&gt;To place a checkpoint in your code, use the method &lt;code&gt;passCheckpoint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open &lt;em&gt;SMViewController.m&lt;/em&gt;, import the TestFlight framework:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405014&quot;&gt;
&lt;td id=&quot;p34050code14&quot;&gt;
&lt;pre&gt;#import &quot;TestFlight.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now insert the following code at the end of &lt;code&gt;viewDidLoad&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;/div&gt;
&lt;blockquote&gt;[TestFlight passCheckpoint:[&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; stringWithFormat:@&quot;view loaded with %i pizzas&quot;, pizzaOrder.count]];&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf6.png&quot;&gt;
&lt;img title=&quot;Checkpoint on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf6-700x332.png&quot; alt=&quot;Checkpoint on TestFlight&quot; width=&quot;700&quot; height=&quot;332&quot; /&gt;&lt;/a&gt;Create a new build and distribute it. Install it on your device, run it, scroll down the table view to make it crash, and restart the app to allow the sending of logs to the server. Now open the dashboard, select the new build and click the &lt;em&gt;Checkpoints&lt;/em&gt; tab on the left to see the list of checkpoints. You’ll see something similar to the screenshot below:&lt;/p&gt;

&lt;p&gt;If you click the &lt;em&gt;Crashes&lt;/em&gt; tab, the second bug report will appear:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf7.png&quot;&gt;&lt;img title=&quot;The second bug on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf7-700x76.png&quot; alt=&quot;The second bug on TestFlight&quot; width=&quot;700&quot; height=&quot;76&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TestFlight offers a feature which is especially nice during beta testing – collecting feedback from your users directly in your app! Open &lt;em&gt;SMViewController.m&lt;/em&gt;, and the following import statement to the top of the file:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405016&quot;&gt;
&lt;td id=&quot;p34050code16&quot;&gt;
&lt;pre&gt;#import &quot;TestFlight.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now modify &lt;em&gt;tableView:commitEditingStyle:forRowAtIndexPath:&lt;/em&gt; as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/&quot;&gt;NSIndexPath&lt;/a&gt; *)indexPath&lt;/div&gt;
&lt;div&gt;{&lt;/div&gt;
&lt;div&gt;[TestFlight openFeedbackView];&lt;/div&gt;
&lt;div&gt;}&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Create a new build and upload it. Open the app, swipe, and tap to delete. Once the delete button is tapped, a view controller like the following will appear:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf8.png&quot;&gt;&lt;img title=&quot;Feedback view on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf8.png&quot; alt=&quot;Feedback view on TestFlight&quot; width=&quot;320&quot; height=&quot;568&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here the user can send you direct feedback about the current build. You can see the messages sent by users in the “Feedback” section of your build, as so:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf9.png&quot;&gt;&lt;img title=&quot;Feedback on TestFlight&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/tf9-700x227.png&quot; alt=&quot;Feedback on TestFlight&quot; width=&quot;700&quot; height=&quot;227&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;TestFlight — Summing it Up&lt;/h2&gt;
&lt;p&gt;TestFlight is a complete service that covers distribution, crash reporting and remote logging. However, I have noticed that sometimes the dSYM is not uploaded successfully by the desktop application and you’ll have to re-upload it using the website.&lt;/p&gt;
&lt;h1&gt;HockeyApp&lt;/h1&gt;
&lt;p&gt;HockeyApp is pretty well known in the indie developer world — probably because it’s made by indie developers! :]&lt;/p&gt;

&lt;p&gt;HockeyApp supports iOS, Android, MacOS, and Windows Phone. HockeyApp, like TestFlight, is more than a crash reporting tool. It also allows you to manage the distribution of builds to beta testers, as well as providing a platform to collect feedback.&lt;/p&gt;
&lt;h2&gt;HockeyApp — Configuring the Project&lt;/h2&gt;
&lt;p&gt;Once you have logged into the site, you will be greeted by an empty dashboard like so:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h1.png&quot;&gt;&lt;img title=&quot;HockeyApp empty dashboard&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h1-700x219.png&quot; alt=&quot;HockeyApp empty dashboard&quot; width=&quot;700&quot; height=&quot;219&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create a new app using the web site, but using the Desktop application is far easier. Here’s the steps to create your new app with HockeyApp:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Create an API token.&lt;/li&gt;
	&lt;li&gt;Download and install the desktop app.&lt;/li&gt;
	&lt;li&gt;Configure your project with the API token.&lt;/li&gt;
	&lt;li&gt;Configure your Xcode project.&lt;/li&gt;
	&lt;li&gt;Archive the project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First, click your account name on the top right and select “API Tokens”, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h2.png&quot;&gt;&lt;img title=&quot;Create API token on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h2.png&quot; alt=&quot;Create API token on HockeyApp&quot; width=&quot;236&quot; height=&quot;305&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you can set the access privileges to the platform in the dialog presented below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h3.png&quot;&gt;&lt;img title=&quot;API token settings on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h3.png&quot; alt=&quot;API token settings on HockeyApp&quot; width=&quot;302&quot; height=&quot;306&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the settings at their defaults (All Apps and Full Access) so that the API key will give you what is effectively “root access” to the platform. Click “Create” and the API token will appear. Copy it and save it somewhere.&lt;/p&gt;

&lt;p&gt;Unfortunately there is no direct download link for the desktop application, but the link should be available in the “Installation” section on this &lt;a href=&quot;http://support.hockeyapp.net/kb/how-tos-faq/how-to-upload-to-hockeyapp-on-a-mac&quot; target=&quot;_blank&quot;&gt;knowledge base page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run the app and paste the API token in the Preferences pane, as shown here:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h4.png&quot;&gt;&lt;img title=&quot;Configuring the HockeyApp desktop application&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h4.png&quot; alt=&quot;Configuring the HockeyApp desktop application&quot; width=&quot;517&quot; height=&quot;277&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How cool would it be if after every archive was built in Xcode, the HockeyApp desktop app opened automatically, ready to upload the new build? Well, you can do that!&lt;/p&gt;

&lt;p&gt;Copy your unmodified starter project and open it in Xcode. Select the root of the project, then select &lt;em&gt;Product/Scheme/Edit Scheme&lt;/em&gt; (or CMD+&amp;lt;), expand the &lt;em&gt;Archive&lt;/em&gt; action, and finally select &lt;em&gt;Post-actions&lt;/em&gt;, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h5.png&quot;&gt;&lt;img title=&quot;Adding a custom action to the archive phase&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h5-700x397.png&quot; alt=&quot;Adding a custom action to the archive phase&quot; width=&quot;700&quot; height=&quot;397&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;em&gt;+&lt;/em&gt; button on the bottom left, select &lt;em&gt;New Run Script Action&lt;/em&gt; and paste the following command into the field shown below:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405018&quot;&gt;
&lt;td id=&quot;p34050code18&quot;&gt;
&lt;pre&gt;open -a HockeyApp &quot;${ARCHIVE_PATH}&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h6.png&quot;&gt;&lt;img title=&quot;New action after archive&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h6.png&quot; alt=&quot;New action after archive&quot; width=&quot;692&quot; height=&quot;469&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click OK to save. Now archive your current project. The desktop application should detect the archive and show the following dialog:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h7.png&quot;&gt;&lt;img title=&quot;The desktop app of HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h7.png&quot; alt=&quot;The desktop app of HockeyApp&quot; width=&quot;507&quot; height=&quot;496&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the desktop application has automatically detected the applicationID, version number and has located both .ipa and .dSYM files to be uploaded on the server. Make sure “Download Allowed” is checked, and click &lt;em&gt;Upload&lt;/em&gt; to send it to the server.&lt;/p&gt;

&lt;p&gt;Open the dashboard and click on the build you just uploaded, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h8.png&quot;&gt;&lt;img title=&quot;The new build on HockeApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h8-700x316.png&quot; alt=&quot;The new build on HockeApp&quot; width=&quot;700&quot; height=&quot;316&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that users and devices have been automatically detected. Neat! Copy the “App ID” and save it somewhere. You’ll need it to configure the crash reporting system. Now the application is ready to be distributed. You’ll find the download phase to be quite streamlined as well.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href=&quot;http://config.hockeyapp.net/&quot; target=&quot;_blank&quot;&gt;http://config.hockeyapp.net/&lt;/a&gt; on your iPhone and tap the “Install” link. This will ask you to install the HockeyApp profile and register your device. It will also install a webclip that will appear as an application icon on your device. Once that’s complete, you’ll be able to see the list of applications available for your device, as below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h9.png&quot;&gt;&lt;img title=&quot;Build distribution on the iPhone&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h9.png&quot; alt=&quot;Build distribution on the iPhone&quot; width=&quot;320&quot; height=&quot;568&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tap your app to install it and check that it runs correctly.&lt;/p&gt;

&lt;p&gt;So far you have been dealing just with the distribution part. It’s time to integrate crash reporting. Head to &lt;a href=&quot;http://hockeyapp.net/releases/&quot;&gt;http://hockeyapp.net/releases/&lt;/a&gt; to download the client SDK for iOS (the current version as of this writing is 3.0). Download the binary version and unzip it. Drag and drop the folder &lt;em&gt;HockeySDK.embeddedframework&lt;/em&gt; onto your Xcode project and make sure “Copy items into destination group’s folder” is checked.&lt;/p&gt;

&lt;p&gt;Select the root in &lt;em&gt;Project Navigator&lt;/em&gt;, select &lt;em&gt;Project&lt;/em&gt; and in the &lt;em&gt;Info&lt;/em&gt; tab set &lt;em&gt;Configurations&lt;/em&gt; to “HockeySDK” as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h10.png&quot;&gt;&lt;img title=&quot;Project configuration for HockeApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h10-700x229.png&quot; alt=&quot;Project configuration for HockeApp&quot; width=&quot;700&quot; height=&quot;229&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;em&gt;SMAppDelegate.h&lt;/em&gt; and add the following import to the top of the file:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405019&quot;&gt;
&lt;td id=&quot;p34050code19&quot;&gt;
&lt;pre&gt;#import &amp;lt;HockeySDK/HockeySDK.h&amp;gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;You now need to implement the HockeyApp protocols in the delegate. Open &lt;em&gt;SMAppDelegate.h&lt;/em&gt; and modify the delegate interface as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;@interface SMAppDelegate : UIResponder &amp;lt;UIApplicationDelegate,BITHockeyManagerDelegate, BITUpdateManagerDelegate, BITCrashManagerDelegate&amp;gt;&lt;/div&gt;
&lt;div&gt;...&lt;/div&gt;
&lt;div&gt;@end&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Next, open &lt;em&gt;SMAppDelegate.m&lt;/em&gt; and add the following code to the beginning of &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt;, using the App ID you saved earlier:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;[[BITHockeyManager sharedHockeyManager] configureWithIdentifier:@&quot;YOUR APP ID&quot; delegate:self];&lt;/div&gt;
&lt;div&gt;[[BITHockeyManager sharedHockeyManager] startManager];&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Add the following method to &lt;em&gt;SMAppDelegate.m&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;- (&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)customDeviceIdentifierForUpdateManager:(BITUpdateManager *)updateManager {
#ifndef CONFIGURATION_AppStore
  if ([[UIDevice currentDevice] respondsToSelector:@selector(uniqueIdentifier)])
    return [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)];
#endif
  return nil;
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;All of this code will allow you to collect data about the user installations only when the build is &lt;i&gt;not&lt;/i&gt; targeting the App Store; that is, when you are in the beta testing phase. To distinguish your new app version from the old version, update the app version in &lt;em&gt;APPNAME-Info.plist&lt;/em&gt;. Now you are ready to test your first crash report! First, archive your application and use the desktop application to upload it to the server.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; If you have already uploaded a version of the app to HockeyApp, you will need to increase the version number of the app. To do so, click on the root project. Select “crashy” underneath “Targets”, click the “Summary” tab, then increase your version number, as so:

&lt;img title=&quot;increase_version_number.png&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/increase_version_number.png&quot; alt=&quot;Increase Version Number&quot; width=&quot;600&quot; height=&quot;252&quot; border=&quot;0&quot; /&gt;

&lt;/div&gt;
&lt;p&gt;On your device, delete the previous version of your pizza app, and open the web clip that was previously installed to your device. Alternately, you can visit &lt;a href=&quot;https://rink.hockeyapp.net/apps&quot;&gt;https://rink.hockeyapp.net/apps&lt;/a&gt; to do the same thing. There you will see the new release.&lt;/p&gt;

&lt;p&gt;Install the new app version, run it, make it crash by deleting a row, and restart the app to allow the crash reports to be uploaded to the sever. After the restart, the application will show an alert view to ask if you want to send a crash report. To do so, tap &lt;em&gt;Send Report&lt;/em&gt;. Now visit the HockeyApp dashboard and select your build. It will appear like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h11.png&quot;&gt;&lt;img title=&quot;The new build on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h11-700x232.png&quot; alt=&quot;The new build on HockeyApp&quot; width=&quot;700&quot; height=&quot;232&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two versions of the app, as expected, and the newest version shows a crash. Click the row corresponding to the newest version and click the &lt;em&gt;Crashes&lt;/em&gt; tab at the top, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h12.png&quot;&gt;&lt;img title=&quot;The first crash log on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h12-700x182.png&quot; alt=&quot;The first crash log on HockeyApp&quot; width=&quot;700&quot; height=&quot;182&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the crash report has already been symbolicated and shows the line responsible for the crash. Click it and it will show the details of the stack trace, as below:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h13.png&quot;&gt;&lt;img title=&quot;Details of the first bug on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h13-e1362390880938.png&quot; alt=&quot;Details of the first bug on HockeyApp&quot; width=&quot;699&quot; height=&quot;737&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have fixed the bug, you can close it using the status drop-down at the top left. For the next crash, you will implement some logging. HockeyApp does not include a built-in logger so you will need to add one. You will use the well-known &lt;a href=&quot;https://github.com/robbiehanson/CocoaLumberjack&quot; target=&quot;_blank&quot;&gt;CocoaLumberjack&lt;/a&gt;. Download the zip file from GitHub, unzip it, and drag and drop the folder named “Lumberjack” onto the project root.&lt;/p&gt;

&lt;p&gt;Now open &lt;em&gt;SMAppDelegate.m&lt;/em&gt; and add the following import statements at the top:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405023&quot;&gt;
&lt;td id=&quot;p34050code23&quot;&gt;
&lt;pre&gt;#import &quot;DDLog.h&quot;
#import &quot;DDASLLogger.h&quot;
#import &quot;DDTTYLogger.h&quot;
#import &quot;DDFileLogger.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Directly below the import statements in &lt;em&gt;SMAppDelegate.m&lt;/em&gt;, add the following code:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405024&quot;&gt;
&lt;td id=&quot;p34050code24&quot;&gt;
&lt;pre&gt;@interface SMAppDelegate ()
@property (nonatomic, strong) DDFileLogger *fileLogger;
@end&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Still working in &lt;em&gt;SMAppDelegate.m&lt;/em&gt;, add the following code to &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt; just before the initialization code for HockeyApp:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405025&quot;&gt;
&lt;td id=&quot;p34050code25&quot;&gt;
&lt;pre&gt;_fileLogger = [[DDFileLogger alloc] init];
_fileLogger.maximumFileSize = (1024 * 64);
_fileLogger.logFileManager.maximumNumberOfLogFiles = 1;
[_fileLogger rollLogFile];
[DDLog addLogger:_fileLogger];

[DDLog addLogger:[DDASLLogger sharedInstance]];
[DDLog addLogger:[DDTTYLogger sharedInstance]];&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Now add the following two methods to &lt;em&gt;SMAppDelegate.m&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;- (&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *) getLogFilesContentWithMaxSize:(NSInteger)maxSize {
  &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableString_Class/&quot;&gt;NSMutableString&lt;/a&gt; *description = [&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableString_Class/&quot;&gt;NSMutableString&lt;/a&gt; string];

  &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/&quot;&gt;NSArray&lt;/a&gt; *sortedLogFileInfos = [[_fileLogger logFileManager] sortedLogFileInfos];
  NSUInteger count = [sortedLogFileInfos count];

  for (NSInteger index = count - 1; index &amp;gt;= 0; index--) {
    DDLogFileInfo *logFileInfo = [sortedLogFileInfos objectAtIndex:index];

    &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/&quot;&gt;NSData&lt;/a&gt; *logData = [[&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/&quot;&gt;NSFileManager&lt;/a&gt; defaultManager] contentsAtPath:[logFileInfo filePath]];
    if ([logData length] &amp;gt; 0) {
      &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *result = [[&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; alloc] initWithBytes:[logData bytes]
                                                  length:[logData length]
                                                encoding: NSUTF8StringEncoding];

      [description appendString:result];
    }
  }

  if ([description length] &amp;gt; maxSize) {
    description = (&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableString_Class/&quot;&gt;NSMutableString&lt;/a&gt; *)[description substringWithRange:NSMakeRange([description length]-maxSize-1, maxSize)];
  }

  return description;
}

- (&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *)applicationLogForCrashManager:(BITCrashManager *)crashManager {
    &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/&quot;&gt;NSString&lt;/a&gt; *description = [self getLogFilesContentWithMaxSize:5000];
    if ([description length] == 0) {
        return nil;
    } else {
        return description;
    }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;The first method retrieves logged contents from a local file, while the second method implements a delegate method for the crash manager of HockeyApp.&lt;/p&gt;

&lt;p&gt;The application is now set up to collect logs locally using the Lumberjack framework, and to send them to the server via the HockeyApp framework.&lt;/p&gt;

&lt;p&gt;Open &lt;em&gt;SMViewController.m&lt;/em&gt; and add the following import:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405027&quot;&gt;
&lt;td id=&quot;p34050code27&quot;&gt;
&lt;pre&gt;#import &quot;DDLog.h&quot;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;em&gt;SMViewController.m&lt;/em&gt; add the following statement to the end of &lt;code&gt;viewDidLoad&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405028&quot;&gt;
&lt;td id=&quot;p34050code28&quot;&gt;
&lt;pre&gt;DDLogVerbose(@&quot;SMViewController did load&quot;);&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;This small logging statement will tell us if the view loaded successfully or not. But as it stands, you won’t actually be able to see that log line because the log level has not been set. Add the following code directly after the &lt;code&gt;#import&lt;/code&gt; statements in &lt;em&gt;SMViewController.m&lt;/em&gt;.&lt;/p&gt;
&lt;div&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr id=&quot;p3405029&quot;&gt;
&lt;td id=&quot;p34050code29&quot;&gt;
&lt;pre&gt;static const int ddLogLevel = LOG_LEVEL_VERBOSE;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;I generally set the logging level to verbose, since I prefer to have as many details as possible when debugging.&lt;/p&gt;

&lt;p&gt;Finally, modify &lt;code&gt;tableView:willDisplayCell:forRowAtIndexPath:&lt;/code&gt; to reflect the code below:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/&quot;&gt;NSIndexPath&lt;/a&gt; *)indexPath {
    if (indexPath.row == pizzaOrder.count-1) {
        DDLogError(@&quot;willDisplayCell - pizzas are %i&quot;, pizzaOrder.count);
        [[&lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/&quot;&gt;NSNotificationCenter&lt;/a&gt; defaultCenter] postNotificationName:LOAD_MORE_NOTIFICATION
                                                            object:nil];
    }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;/div&gt;
&lt;p&gt;This will log the number of pizzas before the notification is posted.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; &lt;code&gt;DDLogError&lt;/code&gt; is the only macro that works synchronously, so you can call it even right before a crash. All the other methods such as &lt;code&gt;DDLogVerbose&lt;/code&gt; and &lt;code&gt;DDLogInfo&lt;/code&gt; are asynchronous, so they might not do their job if they are called immediately before a crash.

&lt;/div&gt;
&lt;p&gt;Now update the application version, archive it and let the desktop application upload it to the server. Delete the old version on the device and install the new one using the web clip.&lt;/p&gt;
&lt;div&gt;

&lt;em&gt;Note:&lt;/em&gt; HockeyApp will detect older versions of a build and will prompt you to update the build from within the app itself. This happens behind the scenes once you upload a new version, so users will not have to manually visit a web address. They just need to launch the app. Pretty convenient, eh?

&lt;/div&gt;
&lt;p&gt;Run the application, scroll to the bottom to make it crash, and restart the app to send the crash report. Now visit the dashboard of your app and you will see the crash reported as such:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h14.png&quot;&gt;&lt;img title=&quot;The second crash log on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h14-700x197.png&quot; alt=&quot;The second crash log on HockeyApp&quot; width=&quot;700&quot; height=&quot;197&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Return to the HockeyApp dashboard. Click on the crash to get to the detailed view, then click &lt;em&gt;Crash Logs&lt;/em&gt; in the tabs above. This will show the raw log. You should see three tabs below the crashes. Click the &lt;em&gt;Description&lt;/em&gt; tab and lo and behold, there are your log statements:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h15.png&quot;&gt;&lt;img title=&quot;Log statements on HockeyApp&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/03/h15-700x319.png&quot; alt=&quot;Log statements on HockeyApp&quot; width=&quot;700&quot; height=&quot;319&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes applications crash during startup. That makes it impossible to send reports to the server, because the application literally has no time to detect a previous crash and send it over-the-air. HockeyApp is the only tool that allows you to delay the initialization phase (and thus delaying the crash) and gives precedence to the “send to server” procedure so that your crash reports can be uploaded.&lt;/p&gt;

&lt;p&gt;You can find some more details on handling startup crashes with HockeyApp &lt;a href=&quot;http://support.hockeyapp.net/kb/how-tos-faq/how-to-handle-crashes-during-startup-on-ios&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;HockeyApp — Summing it Up&lt;/h2&gt;
&lt;p&gt;HockeyApp is a great tool to manage distribution, remote logging and crash reporting. A disadvantage of the service is that it does not include built-in logging. I really appreciated the automation of hooking up with the archive action of Xcode, which saves you some time and stress when you create a new build.&lt;/p&gt;

&lt;p&gt;Sometimes the information you need is a bit hidden, such as remote logs, and requires quite a number of clicks to be displayed. I have already expressed my preference for Crashlytics if you need just crash reporting and logging, but I’d definitely consider HockeyApp if you need also distribution management.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For your benefit, I’ll repeat the comparison table included in the first part of the tutorial:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/table.png&quot;&gt;&lt;img title=&quot;comparison table of crash reporting tools&quot; src=&quot;http://www.raywenderlich.com/wp-content/uploads/2013/05/table.png&quot; alt=&quot;comparison table of crash reporting tools&quot; width=&quot;700&quot; height=&quot;600&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ll confirm the verdict that I expressed in the first tutorial: Crashlytics is an excellent solution if you need to be up and running quickly and you like a well done back-end.&lt;/p&gt;

&lt;p&gt;If you need more than just crash reports and remote logging, and if you are targeting platforms other than iOS, HockeyApp and Bugsense will work really well for you.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="Android" /><category term="crash" /><category term="IOS" /><summary type="html">Believe it or not, developers are not perfect, and every once in a while you might have a (gasp!) bug in your app. You will try your best to ship your apps with no bugs in them, but more often than not you realise afterwards that a bug has slipped through the net. Sometimes such bugs result in crashes, which no user likes to encounter.</summary></entry><entry><title type="html">Android: java.lang.IllegalArgumentException: No config chosen</title><link href="/blog/2014/07/13/android-java-lang-illegalargumentexception.html" rel="alternate" type="text/html" title="Android: java.lang.IllegalArgumentException: No config chosen" /><published>2014-07-13T09:34:00+00:00</published><updated>2014-07-13T09:34:00+00:00</updated><id>/blog/2014/07/13/android-java-lang-illegalargumentexception</id><content type="html" xml:base="/blog/2014/07/13/android-java-lang-illegalargumentexception.html">&lt;p&gt;Simulator:&lt;/p&gt;

&lt;p&gt;gLSurfaceView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);&lt;/p&gt;

&lt;p&gt;Device:&lt;/p&gt;

&lt;p&gt;gLSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);&lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="Android" /><category term="cocos2d" /><category term="cocos2d-x" /><summary type="html">Simulator: gLSurfaceView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0); Device: gLSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);</summary></entry><entry><title type="html">Android 判断当前是否在模拟器中运行</title><link href="/blog/2014/07/13/android-run-in-emulator.html" rel="alternate" type="text/html" title="Android 判断当前是否在模拟器中运行" /><published>2014-07-13T09:28:00+00:00</published><updated>2014-07-13T09:28:00+00:00</updated><id>/blog/2014/07/13/android-run-in-emulator</id><content type="html" xml:base="/blog/2014/07/13/android-run-in-emulator.html">&lt;pre&gt;//是否是模拟器。如果返回TRUE，则当前是模拟器，不是返回FALSE
    public static boolean isEmulator(Context context){
        try{
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            String imei = tm.getDeviceId();
            if (imei != null &amp;amp;&amp;amp; imei.equals(&quot;000000000000000&quot;)){
                return true;
            }
            return  (Build.MODEL.equals(&quot;sdk&quot;)) || (Build.MODEL.equals(&quot;google_sdk&quot;));
        }catch (Exception ioe) { 

        }
        return false;
    }&lt;/pre&gt;</content><author><name>ray</name></author><category term="Blog" /><category term="Android" /><summary type="html">//是否是模拟器。如果返回TRUE，则当前是模拟器，不是返回FALSE     public static boolean isEmulator(Context context){         try{             TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);             String imei = tm.getDeviceId();             if (imei != null &amp;amp;&amp;amp; imei.equals(&quot;000000000000000&quot;)){                 return true;             }             return  (Build.MODEL.equals(&quot;sdk&quot;)) || (Build.MODEL.equals(&quot;google_sdk&quot;));         }catch (Exception ioe) {         }         return false;     }</summary></entry><entry><title type="html">Android SDK Manager 更新慢解决</title><link href="/blog/2014/07/04/android-sdk-manager-update-slow.html" rel="alternate" type="text/html" title="Android SDK Manager 更新慢解决" /><published>2014-07-04T22:51:00+00:00</published><updated>2014-07-04T22:51:00+00:00</updated><id>/blog/2014/07/04/android-sdk-manager-update-slow</id><content type="html" xml:base="/blog/2014/07/04/android-sdk-manager-update-slow.html">&lt;p&gt;1、如果是windows7，那么”开始 - 所有程序 - Android SDK Tools, 右键SDK Manager - 以管理员身份运行(A)”
2、在SDK Manager窗口中，Tools - Options, 打开Settings
1) 在Misc下选中Force https://…sources to be fetched using http://…（原来默认使用https，现在强制使用http）
3、打开hosts文件：Windows在C:\WINDOWS\system32\drivers\etc目录下，Linux/MAC用户打开/etc/hosts文件&lt;/p&gt;

&lt;p&gt;sudo vi /etc/hosts&lt;/p&gt;

&lt;p&gt;在文件末尾默认添加星号行内代码：
&lt;strong&gt;**&lt;/strong&gt;&lt;strong&gt;**&lt;/strong&gt;&lt;strong&gt;**&lt;/strong&gt;&lt;strong&gt;**&lt;/strong&gt;&lt;strong&gt;**&lt;/strong&gt;&lt;strong&gt;**&lt;/strong&gt;&lt;em&gt;**&lt;/em&gt;
#Google主页
203.208.46.146 www.google.com
74.125.113.121 developer.android.com
#更新的内容从以下地址下载
203.208.46.146 dl.google.com
203.208.46.146 dl-ssl.google.com&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;</content><author><name>ray</name></author><category term="Blog" /><summary type="html">1、如果是windows7，那么”开始 - 所有程序 - Android SDK Tools, 右键SDK Manager - 以管理员身份运行(A)” 2、在SDK Manager窗口中，Tools - Options, 打开Settings 1) 在Misc下选中Force https://…sources to be fetched using http://…（原来默认使用https，现在强制使用http） 3、打开hosts文件：Windows在C:\WINDOWS\system32\drivers\etc目录下，Linux/MAC用户打开/etc/hosts文件 sudo vi /etc/hosts 在文件末尾默认添加星号行内代码： ************** #Google主页 203.208.46.146 www.google.com 74.125.113.121 developer.android.com #更新的内容从以下地址下载 203.208.46.146 dl.google.com 203.208.46.146 dl-ssl.google.com  </summary></entry></feed>