Categories
iOS

How to fix missing dSYM files on Firebase Crashlytics

So you decided to set up Firebase Crashlytics on your iOS app to be able to track any crashes that may occur, either while testing or in production. You followed the Firebase documentation step by step and did everything correctly. You forced a crash and it appeared on the Firebase console? Great.

You uploaded the app on TestFlight and told your testers to start testing. After some time, you open the Firebase console and notice that there were no crashes recorded and you see a big red sign telling you that you have to upload dSYM files to be able to process those crashes.

But how are you going to do that?

Let’s first explain what a dSYM file is.

Firstly, when Xcode compiles source code to machine code it generates a list of symbols (class names, global variables, method, and function names). That symbol and the information about the file name and the line number where the symbol is declared constitute a debug symbol. Those debug symbols are used both by the Xcode debugger and for generating the crash reports. When you create a debug build these debug symbols are contained inside the compiled binary file.

Archiving an app

When you create a release build (when you archive an app) debug symbols are placed in a dSYM (Debug Symbol) file to reduce the size of the app. Each binary file (main app executable, frameworks, and app extensions) in an app contains its dSYM file. The binary file and its corresponding dSYM file are tied together by a build UUID. This build UUID is unique, even when the same source code gets compiled with a different Xcode version or build settings, a different UUID will get generated. dSYM files are not the only symbol files. When you archive an app with bitcode, the symbols in the dSYM files are replaced by obfuscated symbols to provide privacy for symbol names. A new symbol file bcsymbolmap (bitcode symbol map) gets created and its purpose is to translate these hidden symbol names back to the original symbol names.

Uploading app to the app store

When you upload an app to the app store it is uploaded as a bitcode. Bitcode is an intermediate representation of how the code looks. That bitcode is later converted to an app binary by Apple. Those new binaries now have different UUIDs than dSYMs that were uploaded to Firebase during build time. So new dSYM files get created and we have to manually upload them to Firebase.

How to fix

1. Open Firebase Console and see for what build should you upload the dSYM files

2. Open that build on App Store Connect 

3. Open build metadata, under General Information find the Includes Symbols tab and click on Download dSYM

4. Unzip the downloaded file

5. Write down the paths for the Pods folder of your application, GoogleService-Info.plst file, and the downloaded dSYMs folder

6. Open the terminal and execute the following command

/path/to/pods/directory/FirebaseCrashlytics/upload-symbols-gsp /path/to/GoogleService-Info.plist -p ios /path/to/dSYMs

7.  If you’ve written the command correctly there should be no warnings and errors and the console should output UUIDs of all the dSYM files that have been uploaded. You can check if they match with the ones in your Firebase console.

8. Wait a couple of minutes for Firebase to process the files

And voila, all the crashes should be displayed on the console.

These are the sources I used in the making of this tutorial, hope you found it useful:

https://developer.apple.com/documentation/xcode/building_your_app_to_include_debugging_information

https://www.tutorialspoint.com/what-is-embedded-bitcode-and-what-does-enable-bitcode-do-in-xcode

https://stackoverflow.com/questions/34363485/why-does-recompiling-from-bitcode-make-me-unable-to-symbolicate-in-xcode-ad-hoc

https://stackoverflow.com/questions/54577202/how-to-run-upload-symbols-to-upload-dsyms-as-a-part-of-xcode-build-process

Until the next read, stay well and safe.

Categories
iOS

How to create a QR code widget

Yes, the long-awaited widgets for iOS devices are finally here!

After the big announcement made at Worldwide Developers Conference in June 2020, Apple has released its biggest update regarding the iOS operating system in September 2020 – iOS 14, with the amazing feature that allows its users to create and add widgets to the home screen of their device.

Hoping that we are not joining the party too late, we decided to try it out ourselves and share our thoughts on the way. In this example, we’ll cover how to add a QR code to the widget that can, later on, be used to present any type of data, be it a link or a plain string message.

But wait. Before you sit down and start developing your widget, there are two requirements you need to know about.

  1. Your device or simulator has to run the iOS 14 operating system
  2. Your Xcode has to be updated to version 12.1

Also, note that we used a third-party library to generate the QR code.

Said that, let’s get started on the widget.

Setup

First things first, you will need to add a new target to the project, by clicking on File > New > Target and choose Widget Extension in the iOS tab, as done in the photo below.

After creating the new target it’s time to add the QR code library to the widget extension. I found that the best way to do that was to add the library using the Swift Package Manager. To do so, select your project in the project navigator and switch to the Swift Packages tab. By clicking the + button you will be asked to add the package repository URL (that can be found on the library’s GitHub in the installation section). The last step to incorporate the QR code library to the widget extension is to go to its target, click the + button in the Frameworks and Libraries section and choose it from the suggested list of libraries. 

Implementation

Moving on, we’ll go to the Widget.swift file where we’ll implement the presentation of the QR code on the widget itself. The first thing you may notice about this class is that the code is written in SwiftUI. Keep in mind that even if you haven’t had a chance to cross paths with SwiftUI before, you won’t need to make big changes to the code.

At the top of the class import your QR code library. Find the WidgetEntryView struct and delete the entire code leaving only the entry property and body view. For this example, we used the EFQRCode library for QR code generation. The QR code library creates image of type cgImage, so we created an additional method that allows us to generate the QR code and parse it to the type of UIImage, making it easier to work with and therefore show on the widget.

The last, but not least step is to use the newly generated QR Code image in the body. The resulting code is shown below.

Result

It’s time to see what we’ve done! 

Run your app on the simulator or device, and once it’s running, close it. Press and hold the screen until the apps on your home screen start to jiggle and the subtle + button appears in one of the top corners. Click on it and the new screen will be presented to you, holding all of the disposable widgets on the phone. Find the widget with the QR code, or in case it is not there find the name of your app, choose the size that fits your screen, and voilà! Your widget is done and ready to use.

Categories
iOS

Tutorial: Create a custom alert view

8 steps to your own UIAlertViewController

The UIAlertViewController is a common and simple-to-use UI component that serves for asking users to enter some data or to notify them about certain events. Unfortunately, this class cannot be customized and that’s why, in this tutorial, I will show you how to create your own completely custom UIAlertViewController.

This is what you are going to get:

The code for the project can be seen on:

https://github.com/Crystal-Pigeon/CustomAlert

These are steps you’ll have to take:

  1. Create the UI
  2. Create the class and connect the UI with the class
  3. Present the alert
  4. Cancel the alert
  5. Make the background darker
  6. Move the alert when the keyboard gets shown
  7. Enable the OK button
  8. Pass data from the alert to the ViewController

Create the UI

Create a xib file

The first step is to create a xib (XML Interface Builder) file. In this file you can design your custom UIView, similarly to how you design custom UIViewControllers through storyboards. To create this file right click on your project in the project navigator and click new file. Choose the View template and click next. Give a name to your custom alert view and click next.

Create the layout

Open the newly created xib file. Click on the view and open the Attributes Inspector. Change size from inferred to freeform. Now you can resize the view, set it so that it has approximately the intended size.

Create the layout through the interface builder. You can create whatever UI you need. I created a simple view that contains a title, a textfield, a label with a switch, and two buttons.

Create the class and connect the UI with the class

Create a swift file

To add some logic to the created view, you need to create a corresponding swift file. Right-click on your project in the project navigator and click the new file. Choose Swift file. Name this file the same as you named your xib file.

Create a UIView subclass

In that file create a subclass of UIView. This class will represent your custom alert.


import UIKit

class AlertView: UIView {
 
}

Open the xib file, click on the view and open the Identity Inspector. Set the class to the newly created class.

Now you can connect IBOutlets by dragging the views from the xib file to the swift file.


// MARK: – IBOutlets
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var configurationNameTextField: UITextField!
@IBOutlet weak var switchButton: UISwitch!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var okButton: UIButton!

Create initial methods

Create a method in which you will perform the basic setup of your view. In this example the background color of the alert view is set to white, a light gray border with a width of 1 is added, and a corner radius is set to 4. Also, the okButton is initially disabled.

<code class="language-swift">
    private func setupView() {
    self.backgroundColor = .white
    self.layer.borderColor = UIColor.lightGray.cgColor
    self.layer.borderWidth = 1
    self.layer.cornerRadius = 4
    self.okButton.isEnabled = false
}
</code>

Create a class method which will be called from the ViewController where you will instantiate this alert. If you don’t know, class methods are the same as static methods, but they can be overridden by subclasses, whereas static methods can never be overridden. The reason why this method is a class method and not a regular method is that this method will be called to create a new instance, which means that it cannot be called on some instance which is the case with regular methods.

This method creates the alert from the specified nib file and calls the basic setup method. Make sure that the parameter nibName is the same as the name of your xib file. If you’re wondering what a nib(NeXTSTEP Interface Builder) is, it is essentially the same as xib, it’s just that the xib file is used while developing, whereas nib files are produced when you create a build.

<code class="language-swift">
class func instanceFromNib() -> AlertView {
    let view = UINib(nibName: "AlertView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! AlertView
    view.setupView()
    return view
} 
</code>

Present the alert

To be able to show the alert view on a certain screen we will have to create an instance of the AlertView class. Add a lazy instance of the AlertView to the ViewController that will use it.

A lazy modifier ensures that the initial value will not be calculated until the first time this property gets called. This modifier is usually used when the initial value of a property requires complex computation and therefore should not be initialized if it will not be used. That is the case with our custom alert. There is a possibility that the user will not need it.

A closure is being used to initialize this property. It may look like a computed property but this value is calculated only once, which is different in the case of using a computed property.

In the closure, we will call the instanceFromNib class method that we previously created and set the translatedAutoresizingMaskIntoConstraints property to false. This will allow us to create auto layout constraints for the alert.

<code class="language-swift">
private lazy var alertView: AlertView = {
    let view = AlertView.instanceFromNib()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
</code>

When you want to show the alert on the screen add it as a subview to the main view of the view controller and add constraints. I set the alert in the vertical center of the screen and put 10 point spacing from the right and left edge of the screen.

<code class="language-swift">
@IBAction func openAlert(_ sender: Any) {
      self.view.addSubview(alertView)
    NSLayoutConstraint.activate([
        alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
        alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
            alertView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0)
    ])
}
</code>
And now a simple AlertView is created.

… But the cancel button does not work.

Let’s fix it.

Cancel the alert

A great practice to make your custom views reusable in different ViewControllers is to create a delegate protocol. That way, any ViewController that conforms to that protocol will be able to use this alert. Create the delegate protocol and declare a method for removing the alert. 

<code class="language-swift">
protocol AlertViewDelegate {
    func removeAlert(sender: AlertView)
}
</code>

Add an optional delegate property to your alert class. This property has to be public because it will be initialized in the ViewController where the alert gets used.

<code class="language-swift">
// MARK: - Properties
var delegate: AlertViewDelegate?
</code> 

Drag an IBAction from the cancel button and call the removeAlert method of the delegate.

<code class="language-swift">
@IBAction func cancel(_ sender: Any) {
    self.delegate?.removeAlert(sender: self)
}
</code>  

The ViewController should implement the alertViewDelegate protocol. All you have to do is remove the alert from superview.

<code class="language-swift">
extension ViewController: AlertViewDelegate {
    func removeAlert(sender: AlertView) {
        sender.removeFromSuperview()
    }
}
</code>

Set the delegate property of the alert to self when initializing it in the ViewController.

<code class="language-swift">
private lazy var alertView: AlertView = {
    let view = AlertView.instanceFromNib()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.delegate = self
    return view
}()
</code>

Ok, the cancel button now works, but would it not be nice if the background got a little darker so that the alert stands out more?

Make the background darker

Create a UIView called backgroundView in the ViewController. Set the background color and the alpha to your linking. Also set the translatesAutoresizingMaskIntoConstraints property to true, so that we can add our own constraints.

<code class="language-swift">
private lazy var backgroundView: UIView = {
    let view = UIView()
    view.backgroundColor = .black
    view.alpha = 0.5
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
</code>

Just before the code for showing the alert view add this code. First we will add the background as a subview and then pin the background to the edges of the view.

<code class="language-swift">
self.view.addSubview(backgroundView)
NSLayoutConstraint.activate([
            backgroundView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
            backgroundView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0),
            backgroundView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
            backgroundView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
        ])
</code>

Also, you will have to remove the background view when the alert gets canceled. Add this line of code in the view controllers implementation of the AlertViewDelegate protocol’s method removeAlert()

<code class="language-swift">
self.backgroundView.removeFromSuperview()
</code>
Now the alert stands out much better.

But how can a user now click on cancel when the keyboard is shown?

Let’s add code to fix this case.

Move the alert when the keyboard gets shown

We could move the alert up by adding a constant to the constraint that centers the alert view vertically. But right now that constraint is not reachable anywhere in the class. Therefore, let’s create a property that will represent that constraint.

Create a constraint property

Declare a new property alertViewConstraint in the view controller.

<code class="language-swift">
var alertViewConstraint: NSLayoutConstraint!
</code>

Instantiate it in viewDidLoad method. This constraint defines that the alert is centered vertically in the view controller.

<code class="language-swift">
override func viewDidLoad() {
    super.viewDidLoad()
    self.alertViewConstraint = NSLayoutConstraint(item: self.alertView, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
}
</code>

In the method where you show the alert replace this:

<code class="language-swift">
NSLayoutConstraint.activate([
    alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
    alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
    alertView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0)
])
</code>

… With this:

<code class="language-swift">
NSLayoutConstraint.activate([
    alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
    alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
    alertViewConstraint
])
</code>

The alert should work the same as it did before these changes.

Move the alert view

Next, create two methods. The first one will be called when the keyboard gets shown and in this method, we will raise the alert view up by 100 points.

The second method will be called when the keyboard disappears from the screen, and the alert will be put back in its original position.

You can move the keyboard by fewer or more points, it’s up to you.

<code class="language-swift">
@objc func keyboardWillShow(notification: NSNotification) {
    self.alertViewConstraint.constant = -100
}
   
@objc func keyboardWillHide(notification: NSNotification) {
    self.alertViewConstraint.constant = 0
}
</code>

In the viewdidLoad method add observers for keyboardwillhide and keyboard will show events. And call the method that will set the tap gesture on the screen.

<code class="language-swift">
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
</code>

Hide keyboard when the user taps anywhere on the screen

Add this code to hide the keyboard when the user touches anywhere on the screen.

<code class="language-swift">
func hideKeyboardWhenTappedAround() {
    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
    tap.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tap)
    }
    
@objc func dismissKeyboard() {
    self.view.endEditing(true)
    }
</code>

In viewDidLoad method call the hidekeyboardWhenTappedAround method.

And here’s the final result you’ll get:

Enable the OK button

You should decide when the ok button should be enabled based on your business logic. In this example, the button should be enabled only when there is some text in the text field.

Firstly, extend the String class and add a new property that will check if the string is blank. The reason why I created a new property instead of using the well-known isEmpty property of String is that if the string contains just whitespaces the isEmpty property will return true and that is not what I want here.

<code class="language-swift">
extension String {
    var isBlank: Bool {
        return allSatisfy({ $0.isWhitespace })
    }
}
</code>

Secondly, create a method that will enable the button if the textfield is not blank and disable the button when the textfield is blank.

<code class="language-swift">
@objc private func editingChanged() {
    if self.configurationNameTextField.text!.isBlank {
        self.okButton.isEnabled = false
    } else {
        self.okButton.isEnabled = true
    }
}
</code>

Lastly, add this line to setupView method. Whenever any change happens to the textfield the method editingChanged will get called.

<code class="language-swift">
self.configurationNameTextField.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
</code>
Now the alert view looks like this:

The last thing to do is to transfer data to the view controller.

Pass data from the alert to the ViewController

Add a new method to the delegate protocol. This method will pass data from the alert to the alert’s delegate.

<code class="language-swift">
protocol AlertViewDelegate {
    func removeAlert(sender: AlertView)
    func handleData(name: String, isAllowed: Bool)
}
</code>

Implement the method in the ViewController. In this example, the data will be shown on two labels in the ViewController.

<code class="language-swift">
func handleData(name: String, isAllowed: Bool) {
    configurationName.text = name
    if isAllowed {
        notificationsAllowedLabel.text = "Notifications are allowed"
    } else {
        notificationsAllowedLabel.text = "Notifications are not allowed"
    }
}
</code>

Drag and IBAction from the ok button in the storyboard to the class code. First, call the removeAlert method from the delegate because the alert should disappear from the screen after the user has clicked ok, and then call the handleData methods from the delegate.

<code class="language-swift">
@IBAction func ok(_ sender: Any) {
    self.delegate?.removeAlert(sender: self)
    self.delegate?.handleData(name: self.configurationNameTextField.text!, isAllowed: switchButton.isOn)
}
</code>
Congratulations! Your alert is finished!
Categories
iOS

Why we chose Texture to layout our UI elements

I remember the first time I heard about Texture. It was introduced to me at an early stage in my career as an iOS developer, and just at the moment when I finally got my head wrapped around the idea of how to work with UIKit and its storyboards. Was I excited to try out something new? Yes. Did I think I was ready for such a challenge at the moment? I don’t think I did. But was it worth it? It most definitely was!

Now, let me introduce you to the library we chose to incorporate into our layout routine.

Texture

Each method of building your UI has its advantages and disadvantages, and this one is no exception. One thing I can tell you straight away is that if you are looking for something that will fast forward the process of creating your app’s layout, or make your project smaller, this is not a solution for you. Anyways, you might get a little bit disappointed at first, but I promise you, it pays off later.

Texture requires you to build your layout just like you are building everything else when developing a mobile app – by writing code.  Yes, you read that right. So instead of dragging and dropping your components to the view, you will define them and arrange them in the class.

It is an iOS framework whose main purpose is to optimize your app by making user interfaces thread-safe. What this means is that you will be able to move all your views and view hierarchies on background threads, where you will prepare them before presenting them. This is the main reason why we love using it, as they make our apps faster, and leave the main thread available to focus on the execution of the app’s logic and data management, as well as to immediately respond to user interaction events.

However, Texture is not the only library that’s been developed to set up your layout programmatically. SnapKit is another example of such a library and the idea behind it was to simplify auto layout in iOS app development. Then why did we still chose Texture? Because, even though SnapKit is a library for itself, and it is not an integral part of Swift programing language, it still works with UIViews and uses UIKit to layout its components, thus throws off the app’s performance which is the exact thing we want to avoid.

Texture vs UIKit

Let’s start this comparison with the most obvious difference.

As you probably already know, storyboards let you build your layout by dragging the components onto the view itself, so the most noticeable difference between Texture and storyboards is that, when developing your app with Texture, there is no way to know how your layout will look until you run the app itself. At least at the beginning. 

However, when it comes to the components that Texture works with, there are a lot of similarities with the UIKit. Let me explain why.

Texture’s basic unit is node, from which all other nodes inherit. It represents an abstract over the UIView which is an abstract over the CALayer. Therefore if you know how to work with views, then you already know how to use nodes as well. Of course, in case you want to access node’s view or layer itself, you can do so by calling node.view or node.layer, since they are underlying views, just keep in mind that you have to perform those calls on the main thread.

Here are some examples of nodes and node containers and their equivalents in UIKit.

TextureUIKit
ASDisplayNodeUIView
ASTextNodeUILabel
ASImageNodeUIImageView
ASCellNodeUITableViewCell and UICollectionViewCell
TextureUIKit
ASViewControllerUIViewController
ASTableNodeUITableView
ASCollectionNodeUICollectionView
ASPagerNodeUIPageViewController

Texture vs SwiftUI

SwiftUI is an innovative way to build the UI of your app that uses a flexible box layout system and allows you to see the changes you make instantly on the preview as you type, as well as to see the change you make to preview immediately in your code. Many of its parts directly build on top of the existing UIKit components, but many other parts don’t, as they are new controls rendered by SwiftUI.

When compared to Texture and UIKit, SwiftUI seems to offer the best of both worlds, since it combines the technique of writing your layout through the code and previewing the changes you made so you can keep track of your UI elements the whole time.

Still, the main question remains: Is the advantage of the preview that SwiftUI brings to the table worth sacrificing the overall performance of the app since it also builds and runs its UI on the main thread?

What does this difference mean to us?

Using Texture has allowed us to bring out the quality of the apps we offer, making them more responsive and faster. It has taught us how to think beyond the fact that we have to lay out a bunch of elements on the screen. Also, it made us visualize our work, and see the components before they are put onto the screen. In the end, it showed us how to become more precise and focused, especially when it comes to details we did not think mattered as much.

When it comes to user interaction, now we can make our apps run more smoothly and we have the tool to customize all sorts of things meeting our or client’s likings.

Summary

One of the main things I learned while using Texture is that patience and creativity are the keys to improving layout. In case you were asking yourself why these two of all things, it’s because Google doesn’t know everything. Sure, it will help you in 95% of the time, but those 5% will also occur. And when that moment comes, you’ll need to combine your knowledge and the creative side of your brain to come up with a unique solution for your problem. On your way to finding the solution the patience will join the game as you will need it throughout your journey. 

Of course, if you prefer, you can always add a third party library and solve the problem, but trust me, it will feel much better if you come up with the solution on your own. Considering how adding another library may slow the app down, the result will be more satisfying, especially for the client.

All in all, I hope you found this post interesting and that it taught you something new. I urge you to give it a shot. Try it out and see if it fits your style of app development. Even if you don’t end up liking it, you will for sure learn something new and acquire new experience.