<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samuel Edwin</title>
    <description>The latest articles on DEV Community by Samuel Edwin (@esam091).</description>
    <link>https://dev.to/esam091</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F352055%2F317c12ac-d53e-444b-82b3-d8b40835672e.png</url>
      <title>DEV Community: Samuel Edwin</title>
      <link>https://dev.to/esam091</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/esam091"/>
    <language>en</language>
    <item>
      <title>Getting the most out of your crash reporter</title>
      <dc:creator>Samuel Edwin</dc:creator>
      <pubDate>Wed, 10 Nov 2021 04:13:19 +0000</pubDate>
      <link>https://dev.to/esam091/getting-the-most-out-of-your-crash-reporter-5d1o</link>
      <guid>https://dev.to/esam091/getting-the-most-out-of-your-crash-reporter-5d1o</guid>
      <description>&lt;p&gt;We developers are not perfect humans, we make mistakes from time to time. Crashes are one of the most common problems we encounter because of those mistakes.&lt;/p&gt;

&lt;p&gt;Crash reporters have been an iOS developer's long time friend. They make it easy to find out where things go wrong like the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumsgx1u3q4uluizyqgky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumsgx1u3q4uluizyqgky.png" alt="A stack trace that points to ViewController.buttonDidTap, easy to debug the source of crash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You know immediately that the crash happened in &lt;code&gt;ViewController.btnDidTap&lt;/code&gt;. All you have to do then is to try reproducing the problem in that method.&lt;/p&gt;

&lt;p&gt;In other cases though, crashes can be hard to debug, like this one&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi7r1j7h1ah9n2aubn4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi7r1j7h1ah9n2aubn4k.png" alt="A crash related to UICollectionView, main cause is unknown"&gt;&lt;/a&gt;&lt;br&gt;
This happened when we did something wrong with UICollectionView. This one can tough to debug if we have dozens of view controllers that use UICollectionView. It's not obvious which view controller that caused the crash.&lt;/p&gt;

&lt;p&gt;What should you do in this situation? &lt;br&gt;
&lt;strong&gt;Add more debugging information&lt;/strong&gt;. &lt;br&gt;
A lot of people know how to use Crashlytics, but not many know how to improve the debugging information. &lt;br&gt;
Here are some less well known features of Crashlytics that I'm sure will be useful for you.&lt;/p&gt;
&lt;h4&gt;
  
  
  Note
&lt;/h4&gt;

&lt;p&gt;In this post I'm going to use Firebase Crashlytics as an example because it is the most popular one. Other crash reporters also have similar features so you can still benefit from the techniques I will describe.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Filtering by user id
&lt;/h2&gt;

&lt;p&gt;Imagine how easy it would be if you can just limit the search of your crashes by user id. This is what Crashlytics has already provided.&lt;/p&gt;

&lt;p&gt;Adding user id is only one line of code away. Add this code below whenever your user has logged in or logged out from your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Call this when the user is logged in&lt;/span&gt;
&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Call this when the user is logged out&lt;/span&gt;
&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you added this code, use the search bar to filter crashes made by this user.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ebxpgkzl4fonsvr742p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ebxpgkzl4fonsvr742p.png" alt="Using the search bar to filter crashes made by a specific user"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Adding custom value
&lt;/h2&gt;

&lt;p&gt;Crashlytics has already provided us a lot of useful information such as the iPhone type, OS version, device orientation and so on. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk13ihn3l0ov4u4z2ysoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk13ihn3l0ov4u4z2ysoi.png" alt="Default data provided by Crashlytics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not enough in some cases, and fortunately adding more information also takes only one line of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCustomValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"My key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you're done, it will show up like below. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kjqt4bsl8lyoc1wghbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kjqt4bsl8lyoc1wghbg.png" alt="Debugging information provided by the code snippet above"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some ideas of custom values you should put in Crashlytics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calendar type&lt;/li&gt;
&lt;li&gt;Language&lt;/li&gt;
&lt;li&gt;Region&lt;/li&gt;
&lt;li&gt;Time Zone&lt;/li&gt;
&lt;li&gt;Accessibility settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how the information going to look like&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24f2ejk2gnthmn64httr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24f2ejk2gnthmn64httr.png" alt="Debugging information after putting extra keys and values"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Case study: A complaint from year 2560
&lt;/h3&gt;

&lt;p&gt;A few years ago we received a crash report in a certain view controller. We know where it happened but not what triggered it.&lt;/p&gt;

&lt;p&gt;After looking at the custom values, we noticed a pattern. it is immediately clear that all the crashes happened only when our users use the Buddhist calendar in their phone.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Logs/Breadcrumbs
&lt;/h2&gt;

&lt;p&gt;Another tool that I find useful is the logging capability. It shows events in order of time. This improves debugging speed because we know the steps to reproduce it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uez86h2taes4x7t4dt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uez86h2taes4x7t4dt8.png" alt="Sample log"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where should you add the logs?&lt;/p&gt;
&lt;h3&gt;
  
  
  App life cycle
&lt;/h3&gt;

&lt;p&gt;Some crashes happen not when the user is using the app, but when the app is in background. By logging every life cycle method, you can know for certain when the crash happened.&lt;/p&gt;

&lt;p&gt;Here are some of the app life cycle methods you should log&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;application(didFinishLaunchingWithOptions:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applicationWillResignActive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applicationDidEnterBackground&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applicationWillEnterForeground&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applicationDidBecomeActive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applicationWillTerminate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbikwma3jfzu6ouyhr9ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbikwma3jfzu6ouyhr9ef.png" alt="Crash report after logging app life cycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the above image as an example, you can clearly tell that the crash happened when the app entered the background state.&lt;/p&gt;
&lt;h3&gt;
  
  
  App entry points
&lt;/h3&gt;

&lt;p&gt;The iOS app ecosystem is getting more complex each year. There are a few ways to launch the app besides tapping the icon from the home screen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Universal linking&lt;/li&gt;
&lt;li&gt;Push notification&lt;/li&gt;
&lt;li&gt;Home screen quick action or 3d touch&lt;/li&gt;
&lt;li&gt;Siri Shortcut&lt;/li&gt;
&lt;li&gt;WidgetKit&lt;/li&gt;
&lt;li&gt;And many other ways&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the log when the crash happened when the user is in another app:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5v05k35b3c7t0e8w99i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5v05k35b3c7t0e8w99i.png" alt="Crash happened in background state"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Case study: Can't notify the dead
&lt;/h4&gt;

&lt;p&gt;There was a crash that happened in a very specific condition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The app must be in a killed state&lt;/li&gt;
&lt;li&gt;The crash must be triggered only with a specific push notification payload.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By logging the app life cycle and app entry points, we can see how to reproduce this specific scenario. This would be a chore to find without the help of logging.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkqabsxfo7yqqmf8dhml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkqabsxfo7yqqmf8dhml.png" alt="Crash happened when the app is terminated"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Opening view controllers
&lt;/h3&gt;

&lt;p&gt;Let's go back a bit to the UICollectionView crash I mentioned before.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi7r1j7h1ah9n2aubn4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi7r1j7h1ah9n2aubn4k.png" alt="A crash related to UICollectionView, main cause is unknown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we have a list of previously visited view controllers, finding out where the crash happened would be easy.&lt;/p&gt;

&lt;p&gt;One straightforward way to add the log is by using this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the result. You can tell immediately where the crash happened.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbmxz6v466dior4bwk5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbmxz6v466dior4bwk5w.png" alt="Crashlytics now shows visited view controllers in order before the crash"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  But I have 100 view controllers 😰
&lt;/h4&gt;

&lt;p&gt;Adding the code snippet above for 5 view controllers is not a problem, but it can be tedious when you have 100 view controllers.&lt;/p&gt;

&lt;p&gt;Fortunately there's a solution, you can use swizzling to log all the view controllers.&lt;/p&gt;

&lt;p&gt;For those who never heard of swizzling, it is a way to swap a method implementation with ours. You can find more about it by searching the internet.&lt;/p&gt;

&lt;p&gt;This code snippet below replaces &lt;strong&gt;every&lt;/strong&gt; UIViewController's &lt;code&gt;viewDidAppear&lt;/code&gt; method that is implemented by Apple with our implementation.&lt;br&gt;
Note that I'm using a library called &lt;code&gt;JRSwizzle&lt;/code&gt; in this example to make swizzling quick simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="kt"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This method that will replace Apple's viewDidAppear implementation&lt;/span&gt;
    &lt;span class="kd"&gt;@objc&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;swizzled_viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// call original viewDidAppear&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swizzled_viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Do the swizzling at the start of the app&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;didFinishLaunchingWithOptions&lt;/span&gt; &lt;span class="nv"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LaunchOptionsKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;FirebaseApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// From now on, swizzled_viewDidAppear will be called time a view controller appears on screen&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;UIViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jr_swizzleMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;#selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UIViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:)),&lt;/span&gt;
        &lt;span class="nv"&gt;withMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;#selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UIViewController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;swizzled_viewDidAppear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Crashlytics will automatically log every view controller that is visited by the app 🥳.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Debugging crashes can be difficult sometimes, but crash reporters have some tools that you can use to make them easier.&lt;/p&gt;

&lt;p&gt;If you have been scratching your head figuring how to reproduce this crash, try the techniques described above. I hope it can solve your problems!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Change variable values instantly without rebuilds</title>
      <dc:creator>Samuel Edwin</dc:creator>
      <pubDate>Mon, 25 Oct 2021 19:08:28 +0000</pubDate>
      <link>https://dev.to/esam091/change-variable-values-instantly-without-rebuilds-4lbc</link>
      <guid>https://dev.to/esam091/change-variable-values-instantly-without-rebuilds-4lbc</guid>
      <description>&lt;p&gt;Most of the apps distributed in the App Store requires a back end server. Facebook, Instagram, Twitter, Netflix, Amazon, you name it.&lt;/p&gt;

&lt;p&gt;There are at least two kinds of servers involved when building these apps: production server and testing server.&lt;br&gt;
Testing server is used to prevent unstable features from going to production.&lt;/p&gt;

&lt;p&gt;This practice impacts our development activities. We often need to switch servers depending on the situation. How do we handle changing these servers?&lt;/p&gt;
&lt;h2&gt;
  
  
  The simple way and the complexities it introduced
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;productionURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.myapp.com"&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;stagingURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://staging-api.myapp.com"&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productionURL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is the typical way people do to switch servers, simple and straightforward.&lt;br&gt;
Change the &lt;code&gt;baseURL&lt;/code&gt; to either &lt;code&gt;productionURL&lt;/code&gt; or &lt;code&gt;stagingURL&lt;/code&gt; when you need to, rebuild the app, and see the results. &lt;/p&gt;

&lt;p&gt;While this approach works, there are some problems you need to be aware of:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. It gets slower over time
&lt;/h3&gt;

&lt;p&gt;Your project's build speed will get slower as your codebase grows. &lt;br&gt;
I have reached a point where I have to wait for 3 minutes just to change servers.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WPOBtQ65--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkdjc9j0kbh26zs3qiav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WPOBtQ65--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkdjc9j0kbh26zs3qiav.png" alt="People slacking off waiting for their code to compile" title="The infamous xkcd 303" width="413" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Human error eventually hits hard
&lt;/h3&gt;

&lt;p&gt;When using the above technique, you have to modify your code in order to switch servers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- let baseURL = productionURL
&lt;/span&gt;&lt;span class="gi"&gt;+ // let baseURL = productionURL
+ let baseURL = stagingURL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to be very careful to not let the code above get committed and published to the App Store.&lt;/p&gt;

&lt;p&gt;This of course can be prevented by code review, but humans eventually make mistakes. It is only a matter of time when you let your guard down and published this to production. I did, at some point in my career.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Huge inconvenience for testers
&lt;/h3&gt;

&lt;p&gt;Aside from developers, testers are the ones that also need access to both staging and production servers.&lt;/p&gt;

&lt;p&gt;If you use CI/CD system to distribute test apps, then you need to provide two builds with each build pointing to a different server.&lt;/p&gt;

&lt;p&gt;This is a big waste of time. Builds take twice longer, and also testers need to download the builds again just to switch servers.&lt;/p&gt;

&lt;p&gt;So now you're convinced that even though the simple technique works, it has some drawbacks.&lt;br&gt;
Wouldn't it be nice if we can switch servers instantly?&lt;/p&gt;


&lt;h2&gt;
  
  
  SwiftTweaks to the rescue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/khan/SwiftTweaks"&gt;SwiftTweaks&lt;/a&gt; is a library to change things instantly without having to recompile, and written in ... Swift. &lt;em&gt;I thought it was PHP&lt;/em&gt; 😮.&lt;/p&gt;
&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;p&gt;SwiftTweaks works by showing a pop up when you shake your phone. Choose whatever settings you want to tweak and that's it.&lt;/p&gt;

&lt;p&gt;In our case, SwiftTweaks will show a menu so we can switch between servers instantly. No rebuilds needed.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mDc6nLXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ugu5nju0i1t1xoqaq5k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mDc6nLXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ugu5nju0i1t1xoqaq5k.gif" alt="A video showing how to use SwiftTweaks from the app" width="300" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If this looks appealing to you, then let me show you how it's done.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let's build our own tweaks
&lt;/h3&gt;

&lt;p&gt;Create a new project and add the library as instructed &lt;a href="https://github.com/khan/SwiftTweaks#installation"&gt;here&lt;/a&gt;. &lt;br&gt;
I won't go into details here since this is not a beginner article.&lt;br&gt;
After you're done, we can start the next steps.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Create a tweak store
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AppTweaks.swift&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftTweaks&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;AppTweaks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;TweakLibraryType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// A&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Tweak&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;StringOption&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"General"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Networking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Server URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s"&gt;"https://api.myapp.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;"https://api-staging.myapp.com"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;defaultStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;TweakStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;TweakStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;tweaks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// B&lt;/span&gt;
            &lt;span class="nv"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;TweakDebug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isActive&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This seems like a lot of code but trust me it is simpler than what it looks like.&lt;/p&gt;

&lt;p&gt;There are two things that we do here, as marked in comments A and B.&lt;/p&gt;

&lt;p&gt;In comment A, we are creating a tweakable &lt;code&gt;serverURL&lt;/code&gt; with two available options: &lt;code&gt;https://api.myapp.com&lt;/code&gt;, &lt;code&gt;https://api-staging.myapp.com&lt;/code&gt;. &lt;br&gt;
The first option will be chosen as the default.&lt;br&gt;
This option will show up under the &lt;em&gt;General&lt;/em&gt; &amp;gt; &lt;em&gt;Networking&lt;/em&gt; &amp;gt; &lt;em&gt;Server URL&lt;/em&gt; option. &lt;/p&gt;

&lt;p&gt;In comment B, we are putting all the available options in the &lt;code&gt;tweaks&lt;/code&gt; argument so they can show up in the UI.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Change the default UIWindow
&lt;/h4&gt;

&lt;p&gt;These options will show up when you shake the phone while the app is active.&lt;br&gt;
In order to do that, we need to use the &lt;code&gt;TweakWindow&lt;/code&gt; class provided by SwiftTweaks instead of &lt;code&gt;UIWindow&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;TweakWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIScreen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nv"&gt;tweakStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AppTweaks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;defaultStore&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Create the root view controller as usual&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootViewController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MyViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeKeyAndVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Use the chosen option from the code
&lt;/h4&gt;

&lt;p&gt;Now we need to change how the &lt;code&gt;baseURL&lt;/code&gt; gets its value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- let baseURL = productionURL
&lt;/span&gt;&lt;span class="gi"&gt;+ let baseURL = AppTweaks.assign(AppTweaks.serverURL).value
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of hardcoding the server URLs, we can now just let SwiftTweaks decide which server URL that we want to use.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Restart the app and see the changes
&lt;/h4&gt;

&lt;p&gt;We'll now see how easy it is to change configurations using SwiftTweaks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run your app as usual&lt;/li&gt;
&lt;li&gt;Shake your device (&lt;code&gt;ctrl + cmd + z&lt;/code&gt; from the simulator), a view controller will pop up showing all your configurations.&lt;/li&gt;
&lt;li&gt;Change your selected server from production to staging.&lt;/li&gt;
&lt;li&gt;Restart your app to see the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mDc6nLXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ugu5nju0i1t1xoqaq5k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mDc6nLXk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ugu5nju0i1t1xoqaq5k.gif" alt="A video showing how to use SwiftTweaks from the app" width="300" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! No more code changes needed when you want to switch servers. You can switch servers whenever you like.&lt;/p&gt;

&lt;h3&gt;
  
  
  I don't want my users to use this tool
&lt;/h3&gt;

&lt;p&gt;Me too. God knows what weird things your users will do with this.&lt;/p&gt;

&lt;p&gt;So how do you prevent this tool from leaking to app store builds? &lt;strong&gt;nothing&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Yes, you don't have to do anything. &lt;code&gt;SwiftTweaks&lt;/code&gt; is smart enough to know that this will only show up in debug builds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other useful SwiftTweaks usages
&lt;/h2&gt;

&lt;p&gt;Now you have seen how powerful this tool is. You might be wondering, what else can you use this for? I have some ideas for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toggle onboardings or tutorials
&lt;/h3&gt;

&lt;p&gt;Onboardings or tutorials teach users on how to use your app. It is a very common pattern nowadays.&lt;/p&gt;

&lt;p&gt;The thing with onboardings is they only show up once when the users open the app for the first time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;showOnboarding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;UserDefaults&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;standard&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"has_shown_onboarding"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;showOnboarding&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;UserDefaults&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;standard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"has_shown_onboarding"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootViewController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;OnboardingViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootViewController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MainViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ordinary humans like me can't develop onboardings perfectly on the first try. We have to run the app multiple times until we got it right. &lt;br&gt;
I usually hard coded &lt;code&gt;showOnboarding&lt;/code&gt; to true until I've finished with the development. &lt;/p&gt;

&lt;p&gt;Thanks to &lt;code&gt;SwiftTweaks&lt;/code&gt;, I don't have to do any hard coding anymore. All I have to do is create a new tweak.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;showOnboarding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Tweak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"General"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Onboarding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Show onboarding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;UserDefaults&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;standard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"has_shown_onboarding"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I just need to change the logic for a bit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- let showOnboarding = !UserDefaults
-     .standard
-     .bool(forKey: "has_shown_onboarding")
&lt;/span&gt;&lt;span class="gi"&gt;+ let showOnboarding = 
+    AppTweaks.assign(AppTweaks.alwaysShowOnboarding)
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Toggle SDK loggings
&lt;/h3&gt;

&lt;p&gt;Third party SDKs such as Mixpanel have logging capability to ease debugging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Mixpanel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mainInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loggingEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You mostly want this option to be turned off unless for some occasions. The reason is the logs can pollute your debugging console and make important information harder to find.&lt;/p&gt;

&lt;p&gt;SwiftTweaks lets you toggle this on/off anytime you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;enableMixpanelLogging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Tweak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"General"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Logging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Enable Mixpanel logging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kt"&gt;Mixpanel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mainInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loggingEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="kt"&gt;AppTweaks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AppTweaks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enableMixpanelLogging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We do some small tweaks here and there in our coding activity. Tweaking those configurations doesn't have to be time consuming. SwiftTweaks lets you do small tweaks instantly. This will save you a lot of time.&lt;/p&gt;

&lt;p&gt;There are so many things that you can do with this. You can change colors, fonts, animations on the fly with out having to recompile your code. &lt;/p&gt;

&lt;p&gt;Start using it now see how your quality of life gets better because of it.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Conflict-free Xcode Project Management</title>
      <dc:creator>Samuel Edwin</dc:creator>
      <pubDate>Tue, 05 Oct 2021 10:31:19 +0000</pubDate>
      <link>https://dev.to/esam091/conflict-free-xcode-project-management-78d</link>
      <guid>https://dev.to/esam091/conflict-free-xcode-project-management-78d</guid>
      <description>&lt;p&gt;There is one pain that every iOS developers face, especially when they work in a team. &lt;/p&gt;

&lt;h2&gt;
  
  
  Xcode merge conflicts.
&lt;/h2&gt;

&lt;p&gt;It is one of the most dreadful and time wasting activity that nobody likes to do. This pain only stings more the bigger your team and codebase gets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does it keep happening?
&lt;/h2&gt;

&lt;p&gt;Xcode keeps track of all source files that needs to be compiled within this &lt;code&gt;project.pbxproj&lt;/code&gt;. It is a plain text file which has the content like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// !$*UTF8*$!
{
    archiveVersion = 1;
    classes = {
    };
    objectVersion = 50;
    objects = {

/* Begin PBXBuildFile section */
        923EF325270C588D0058CE0F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923EF324270C588D0058CE0F /* AppDelegate.swift */; };
        923EF327270C588D0058CE0F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923EF326270C588D0058CE0F /* SceneDelegate.swift */; };
        923EF329270C588D0058CE0F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923EF328270C588D0058CE0F /* ViewController.swift */; };
        923EF32C270C588D0058CE0F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 923EF32A270C588D0058CE0F /* Main.storyboard */; };
        923EF32E270C588F0058CE0F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 923EF32D270C588F0058CE0F /* Assets.xcassets */; };
        923EF331270C588F0058CE0F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 923EF32F270C588F0058CE0F /* LaunchScreen.storyboard */; };
        923EF33A270C58E00058CE0F /* ShopViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923EF338270C58E00058CE0F /* ShopViewController.swift */; };
        923EF33B270C58E00058CE0F /* ShopViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 923EF339270C58E00058CE0F /* ShopViewController.xib */; };
/* End PBXBuildFile section */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't even need to be in a team to experience this. If you work on multiple branches, the conflict often shows up whenever you merge them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONFLICT (content): Merge conflict in SadApp.xcodeproj/project.pbxproj
Automatic merge failed; fix conflicts and then commit the result.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Have you ever wished for the merge conflicts to go away?
&lt;/h2&gt;

&lt;p&gt;I do, all the time. I have worked on Android and web projects in the past. The conflicts happen much less frequently, if they happen at all.&lt;/p&gt;

&lt;p&gt;If you have been looking for a solution, you are lucky my friend. There is a hope to end this madness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swift Package Manager
&lt;/h2&gt;

&lt;p&gt;Swift Package Manager(SPM) has existed for years since Swift 3.0. &lt;/p&gt;

&lt;p&gt;We seldom see its usage outside of server side and command line Swift apps. The reason for this is because it used to have bad support for iOS development.&lt;/p&gt;

&lt;p&gt;Things have changed though since the release of Xcode 12. &lt;/p&gt;

&lt;p&gt;Xcode 12 adds a first class support for using Swift Package Manager in iOS app development. &lt;br&gt;
All the pains I mentioned before instantly went away after I used it.&lt;/p&gt;

&lt;p&gt;This is a powerful technique yet still unknown to many people, given how new it is. &lt;/p&gt;

&lt;p&gt;Now I want to show you how to use it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's give it a try
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a new iOS project, as usual
&lt;/h3&gt;

&lt;p&gt;You know the drill:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Xcode&lt;/li&gt;
&lt;li&gt;Create a new iOS app. I'll just name it &lt;code&gt;MyApp&lt;/code&gt; in this demo. Yes I have a wonderful naming sense.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8rhh18je76s69swnedvw.png" alt="Image showing an Xcode project creation"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Create a new Swift Package.
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;From your Xcode, go to File -&amp;gt; New -&amp;gt; Swift Package.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf9mbdg8ybxu61c4288m.png" alt="Swift Package creation in menu bar"&gt;
&lt;/li&gt;
&lt;li&gt;Give a name for your package. Some important things to notice:

&lt;ul&gt;
&lt;li&gt;Make sure that the package will be stored &lt;strong&gt;inside&lt;/strong&gt; your project folder as shown in the highlighted green box below.&lt;/li&gt;
&lt;li&gt;Select your previously created project in the &lt;code&gt;Add to&lt;/code&gt; section as shown in the yellow box below.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl3br6wzsowangan1vmjo.png" alt="Swift Package creation dialogue"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you did it right, the Swift Package will be shown in the file explorer. 
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwb01ggaetqwdsz6p1fz.png" alt="Swift Package is added to the Xcode project"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Import the package from the app target.
&lt;/h3&gt;

&lt;p&gt;In order to import your Swift package from your app, go to your app's &lt;code&gt;Build Phases&lt;/code&gt; section, and add your package target in the &lt;code&gt;Link Binary With Libraries&lt;/code&gt; subsection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjqj3jpfyce8a7vwe0s69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjqj3jpfyce8a7vwe0s69.png" alt="Add the image through the Build Phase setting in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Build the project
&lt;/h3&gt;

&lt;p&gt;Let's see if things work as they should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add new code inside &lt;code&gt;Sources/MyAppPackage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Import the code from your app target.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MyAppPackage/Sources/MyAppPackage/MyAppPackage.swift&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Swift Package"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// MyApp/AppDelegate.swift&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;MyAppPackage&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didFinishLaunchingWithOptions&lt;/span&gt; &lt;span class="nv"&gt;launchOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UIApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;LaunchOptionsKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]?)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you see the &lt;code&gt;Hello from Swift Package&lt;/code&gt; message from your console, then that means you have used a Swift Package code from inside your app. Congratulations 🤝.&lt;/p&gt;

&lt;p&gt;One important thing here &lt;code&gt;project.pbxproj&lt;/code&gt; does not change no matter how many files you put in your package. That's why this technique is miles better compared to the old one.&lt;/p&gt;
&lt;h2&gt;
  
  
  What should I put in the Swift Package?
&lt;/h2&gt;

&lt;p&gt;I'd put pretty much everything there except for Info.plist, Main and LaunchScreen storyboards. &lt;/p&gt;

&lt;p&gt;For everything else I'll put them into the Swift Package: Swift files, Xibs, Storyboards, image assets.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't lose your assets
&lt;/h2&gt;

&lt;p&gt;If you use Xibs, Storyboards, or image assets, you need to do a small tweak in your code. Pretty much things that related to bundles.&lt;/p&gt;

&lt;p&gt;When you create a view controller, this is the initializer that a lot people use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;MyViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIViewController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nibName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that I put nil in the bundle argument. This means that the main Bundle will be used to locate the Nib resource.&lt;/p&gt;

&lt;p&gt;If your Nib is located in a Swift Package like below,&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tsunf8ln441glwu61j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tsunf8ln441glwu61j4.png" alt="The Xib is located in a Swift Package"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you need to change the bundle argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;nibName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Swift Package Manager is a superior way to manage your Xcode projects because you don't have to deal with merge conflicts that often anymore. Things just work.&lt;/p&gt;

&lt;p&gt;If you are starting a new project, I'd recommend you to use it right away.&lt;/p&gt;

&lt;p&gt;If you are working in an existing project, better migrate them to Swift Package Manager as soon as you can.&lt;/p&gt;

&lt;p&gt;If you have questions, let me know in the comments section. I'd love to answer your confusions.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>xcode</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
