Creating iOS Apps without Storyboard – Part 1

What are “nibless” apps?

Apps which are designed without the help of Storyboard are called as “Nibless” apps. Normally we design an app with the help of a Storyboard file. Earlier they were called Xib files or Nib files. Hence the term “Nibless”.

Why should we create Apps without storyboard?

There are a number of reasons.

  1. It makes for a better experience when implementing along with version control.
  2. Allows us to create UI elements dynamically.
  3. Makes reusable UI Components easier to distribute and reuse.

How can we create Apps without Storyboard?

There are a couple of things that need to be done. Firstly the Main.storyboard file needs to be removed and the project settings need to be updated to reflect this change.. We are doing this since we won’t be using the storyboard file.
Everything will now have to be started up by us manually. Many of these tasks were taken care of by storyboard, but since that was removed we will have to do it. This means we have to manually create the window, create the view controller set it as a the root view controller.
We also have to manually create each and every component on our own. That is the very thing we were trying to achieve.

This example is implemented on Xcode 10.3 on macOS 10.14.5. We are not implementing auto layout in this article. We will look at implementing that programmatically in the next article.

  1. Let us start with an empty project. Open Xcode.
  2. Select File > New > Project
  3. Give it any name. Select the language as Swift & leave the checkboxes unchecked.
  4. Once the project loads select the Main.storyboard file and delete it.
  5. Switch to the Project settings file.
  6. Remove the entry for the main interface.
  7. It is a good idea to leave the LaunchScreen.storyboard file. The reason for this is to give the launch process a reference of the screen size it needs to produce. Else it will default down to the 0,0,320,480 which is the old iPhone size.
  8. Switch to the AppDelegate.swift file.
  9. Add the following property below the UI Window declaration.
      
    let mainScreenController : ViewController = ViewController() 
    
  10. Add the code to create the window and set root view controller in the didFinishLaunchingWithOptions method
       
    //1. Create the UIWindow object   
    self.window = UIWindow(frame: UIScreen.main.bounds)   
    
    //2. Set the root view controller   
    self.window?.rootViewController = self.mainScreenController   
    
    //3. Make the window key and visible  
    self.window?.makeKeyAndVisible()  
    
  11. Switch to the ViewController.swift file.
  12. Declare the following variables
      
    //UI Variables  
    var labelDemo   : UILabel?  
    var imageDemo   : UIImageView?  
    var buttonDemo  : UIButton = UIButton(type: UIButton.ButtonType.roundedRect) 
    var dataField   : UITextField?
    
  13. Implement the function to create labels. The process of creating a view programmatically is fairly straightforward. Barring a few variations depending on the view component nothing is drastically different.
      
    func createLabel() 
    {      
         //1. Specify the dimensions      
         let labelRect : CGRect   = CGRect(x: 100.0, y: 50.0, width: self.view.frame.size.width - 130.0, height: 60.0)     
    
         //2. Create the view object      
         labelDemo                = UILabel(frame: labelRect)      
    
         //3. Customise the view attributes      
         labelDemo?.text          = "This is my first Programmatic App."                
         labelDemo?.textColor     = UIColor.yellow      
         labelDemo?.textAlignment = NSTextAlignment.left  
         labelDemo?.numberOfLines = 0      
         labelDemo?.font          = UIFont.boldSystemFont(ofSize: 20.0)      
    
         //4. Add the view to the subview      
         self.view.addSubview(labelDemo!) 
    } 
    
    Let us examine the steps one by one.
     
    //1. Specify the dimensions 
    let labelRect : CGRect = CGRect(x: 100.0, y: 50.0, width: self.view.frame.size.width - 130.0, height: 60.0)
    
    This will define the dimensions of the view. As we are not implementing auto layout we will need to do this manually.
     
    //2. Create the view object
    labelDemo = UILabel(frame: labelRect) 
    
    Now that we have the dimensions we can go ahead and instantiate an instance of the label object using those dimensions. These 2 parts are the same as dragging a label from the object library onto the storyboard and placing it onto the storyboard per our requirements.
    //3. Customise the view attributes 
    labelDemo?.text          = "This is my first Programmatic App."     
    labelDemo?.textColor     = UIColor.yellow 
    labelDemo?.textAlignment = NSTextAlignment.center      
    labelDemo?.numberOfLines = 0 
    labelDemo?.font          = UIFont.boldSystemFont(ofSize: 20.0)
    
    This part is the same as changing the attributes in the attributes inspector. This is where we customise the label.
     
    //4. Add the view to the subview 
    self.view.addSubview(labelDemo!) 
    
    This last part also forms one part of dragging the label on to the storyboard. When we drag a view on to the storyboard it is placed within the main view that belongs to the ViewController. This statement completes the above process.
  14. Repeat the above steps for showing an image.
    func createImage()
    {
         //1. Specify the dimensions
         let imageRect  : CGRect  = CGRect(x: 30.0, y: 50.0, width: 60.0, height: 60.0)
    
         //2. Create the image model
         let imageModel : UIImage = UIImage(named: "logo.png")!
    
         //3. Create the view object
         imageDemo                = UIImageView(frame: imageRect)
    
         //4. Customise the view attributes
         imageDemo?.image         = imageModel
         imageDemo?.contentMode   = UIView.ContentMode.scaleAspectFit
    
         //5. Add the view to the subview
         self.view.addSubview(imageDemo!)
    }
    
    The code above is almost similar to the one created for labels except for the fact that we had to explicitly create a model object for the view. Images being different from strings, require this process to be done explicitly.
  15. Similarly let us implement the code for creating buttons
    func createButton()
    {
         //1. Specify the dimensions
         let buttonRect : CGRect = CGRect(x: 30.0, y: 220.0, width: 100.0, height: 50.0)
    
         //2. Provide the frame to the button
         buttonDemo.frame = buttonRect
    
         //3. Customise the view attributes
         buttonDemo.setTitle("Click Me", for: UIControl.State.normal)
         buttonDemo.addTarget(self, action: #selector(ViewController.clickMeTapped), for: UIControl.Event.touchDown)
    
         //4. Add the view to the subview
         self.view.addSubview(buttonDemo)
    }
    
    @objc func clickMeTapped(
    {
         print("Click me tapped!")
    }
    
    Again just minor variations here. Mainly the step to add a target function to be invoked when the button is tapped. We also need to write the target function itself.
  16. We will also implement the code to create a text field.
    func createTextField()
    {
        //1. Provide dimensions for the view
        let tfRect : CGRect             = CGRect(x: 30.0, y: 140.0, width: self.view.frame.size.width - 60.0, height: 50.0)
            
        //2. Create the view object
        dataField                       = UITextField(frame: tfRect)
            
        //3. Customise the attributes of the view
        dataField?.placeholder          = "Enter Name"
        dataField?.borderStyle          = UITextField.BorderStyle.roundedRect
        dataField?.keyboardType         = UIKeyboardType.namePhonePad
        dataField?.keyboardAppearance   = UIKeyboardAppearance.dark
        dataField?.returnKeyType        = UIReturnKeyType.go
            
        //4. Add the view to the subview
        self.view.addSubview(dataField!)
    }
    
  17. Next we need to call all these functions. I have implemented a single creator function for that.
    func createUIElements()
    {
         self.createLabel()
         self.createImage()
         self.createButton()
         self.createTextField()
    }
    
  18. Lastly we will call this function in the viewDidLoad method. Add the following lines to the viewDidLoad method.
    self.view.backgroundColor = UIColor.lightGray
    self.createUIElements()
    
    I have also added code to change the background colour so that we can see the background clearly.
  19. Run the project. Everything should appear normally.

Are there any benefits of creating apps without storyboard?

The points mentioned in the “why should we make programmatic apps?” section are some of the advantages. Beyond that there aren’t too many.
If you are looking at a team based project development then this approach is good.
There is no difference in terms of memory or performance when it comes down to apps design with or without storyboard.

Are there any drawbacks?

As can be seen from the example above, there are a couple of drawbacks

  1. The main drawback is that you can’t get a quick preview of how your app looks. You have to run the simulation every time you wish to see the end result.
  2. There is a lot more coding involved. Which can be daunting to those who are overly accustomed to designing with the help of storyboards

Note

A small point. I have left the LaunchScreen.storyboard file. I did not delete it. The reason I did that was to allow the app to allow the system to determine the dimensions on the device. If we do delete the file then the UIScreen.main.bounds return (0.0, 0.0, 320.0, 480.0) which are the old iPhone screen size settings.
While you can go ahead and make changes programmatically it is a lot easier to just leave the LaunchScreen.storyboard file there.

Carrying on from the previous point. It actually is okay if you leave the Main.storyboard file as is too. In which case you will have to skip steps 5,6,8,9,10. The code is still running programmatically but you do not have to create the main ViewController manually.

Download the Source Code

You can download the Xcode Project from this link.

Advertisements

When to use Swift & when to use Objective-C?

Over the past few years I have received a number of questions with regards to Swift & Objective-C. Specifically related to the future of the 2. I will try to address those questions in the form of an FAQ.

Should I learn Swift or Objective-C?

This is a question that I get from developers new to iOS/macOS App Development. Ideally speaking, you should learn Swift. As that is going to become the main language for App development on Apple’s ecosystem. However, the reality is a little different. There are a large number of applications that are written in Objective-C. You are likely to encounter them at your workplace. You may also have to maintain, upgrade & improve those apps. In such a case, it makes sense to learn Objective-C too.

Can I mix Swift & Objective-C in the same project?

Yes! But remember that you should check for feature compatibility between the 2 languages. Adding Swift code to an Objective-C project may not be very beneficial as only those features that are compatible with Objective-C can be written in Swift.

Going the other way round is not a problem. You can read more about that here:Mixing Swift & Objective-C

Will Objective-C be deprecated in the future?

That is an interesting question. There is no formal announcement from Apple stating the Objective-C is going to be deprecated. However, one can expect more attention to be paid to Swift. That is where most of the newest techniques, tools & technologies are going to be available. Objective-C will keep running as it is as of now.

Can I mix Swift with other Programming Languages?

Swift can easily be mixed with Objective-C. If you wish to incorporate C++ or C code in your Swift Project then wrapping them in Objective-C code allows you to achieve this.

Apart from that Swift does support working with C code code. You can read about that here:Interacting with C APIs.

Swift does not provide interoperability support for any other languages as of now.

Which version of Swift should I use?

It is recommended that you use the latest available version of Swift. However, the actual version that you work on depends on many other factors like: compatibility with OS Versions, support & business related choices.

Why shouldn’t we just convert all our Objective-C code to Swift and keep things simple?

A very tempting proposition. However, practical realities prevent us from doing this. The process of converting from Objective-C to Swift is time consuming. Apart from having to convert the syntax, the code also needs to be optimised taking into account the new features that are available. This will mean extensive testing and quality assurance. Most companies will not invest their resources into this endeavour.

A better approach is to migrate to Swift gradually. Here are some ways to do this:

  1. If its a brand new product/app that you are creating, start it in Swift.
  2. Any new reusable code components that are being created should be done in Swift (they should be Objective-C compatible if you intend to use this code in Objective-C projects).
  3. If any part of a product is going to undergo heavy change, either due to a bug fix or a new feature. This is a good time to convert it into Swift.

A good example is how Apple is approaching the process of migrating to Swift. They are doing it component by component.

I have been developing apps in Objective-C for some time. I am able to create any reasonably complicated app now. If Objective-C hasn’t been deprecated then should I start making apps in Swift?

This is a choice that you have to make. It is recommended that new apps (at the very least) be made in Swift as that is the language that will undergo the maximum amount of changes & improvements in the future.

What do you suggest as a trainer?

Another question that I get very often. It depends on the situation. I would say learn both Swift & Objective-C. You can skip learning Objective-C if you are confident that you will not have to work with any projects written in that language.

If I am starting on a brand new project I would use Swift. But if its an Objective-C project I would stick to Objective-C.

Can Swift development only be done on macOS?

No! Swift development can also be done on Linux. However, iOS/macOS/tvOS/watchOS App Development can only be done on macOS through Xcode.

How should I migrate to Swift?

There are different approaches that one can use. It all depends on the situation and needs of your organisation. Here are some things that you can do:

  • Start development of brand new apps (from scratch) in Swift.
  • If you are creating a brand new library which will be used for future projects then go ahead with Swift.
  • If a major component of an existing app is going to be changed significantly then you can go ahead with Swift.

You can do all or some of the above. There may be other strategies too. You should also factor in the cost of migration from one language to another.

 

Creating Frameworks for iOS/OS X App Development

Creating Swift Frameworks

Creating Swift Frameworks is easy. The steps below walk you through creating a Swift Framework. The steps below have been performed on Xcode 7.3

  1. Launch Xcode.
  2. Select Create New Project. Or from the menu bar select File > New > Project
  3. From the Template chooser select the Framework & Library  Option under iOS
  4. Select Cocoa Touch Framework1
  5. Give your project a name.
  6. Make sure the language selected is Swift.
  7. Feel free to enter values of your choice for organisation name and organisation identifier.
  8. Save your project. Optionally, if you have a version control repository like Git you may save it there.
  9. In left hand side bar make sure you have selected the Project Navigator.
  10. Within the Project Navigator make sure you have selected the folder named after your project.
  11. Click on File > New > File.
  12. Make sure iOS Source is selected on the left hand side.
  13. Select the file type as Swift.IMG_3525
  14. Write down the code that you want to make available through a framework.
  15. Now this is the key point. Place the keyword public before all the elements that you want to make publicly accessible.Why do we need to do this? To understand this we need to understand the scope of different elements within a typical Swift project. IMG_3521

    Different variables/classes/functions that are declared within a module are accessible freely within the module. Swift files contain code & are themselves found within Swift modules. So a module can mean project or a framework.So, to access the variables/functions/classes from module A in module B, we have to make those elements of module A public in order to access them in module B.

    For more information, do read Apple’s Swift Documentation.

  16. The next steps depend on what your ultimate objective is. If you wish to build a framework for distribution then you need to follow a process that is similar to distributing an app. You need to get the code signing done & prepare the project for distribution.
  17. If however, you plan to release it internally, or even just test it. Then you can follow the steps below.
  18. Firstly, our objective is to make this framework run on both OS X(macOS) as well as iOS.
  19. To do that we will be adding a new target. Click on File > New > Target.
  20. Select OS X & the Frameworks & Libraries from the sidebar.
  21. Select Cocoa Touch Framework
  22. Give your framework a unique name. Something that indicates this framework is for OS X(macOS).
  23. Now, we don’t need to rewrite the code for the Mac. We can simply make the file we have written a member for the OS X Framework Target.
  24. To do that make sure that the right hand side sidebar is visible.
  25. In the left hand side sidebar make sure that you have selected the new Swift file with the code you have written in there.
  26. In the right hand side sidebar select the Document Inspector.
  27. Under Target Membership make sure that both the Targets are checked. The target for iOS should already be checked.IMG_3520
  28. Thats it. If you do not wish to make your code available for both iOS & OS X then skip steps 19 – 27.
  29. The next part is building the framework. We will be building this framework for use internally. We will first build the iOS framework.
  30. From the tool bar, make sure the target selected is for iOS. For the device you can select any device that you wish.
  31. Then click on Product > Build to build the framework. If all goes well then you should get the message Build Succeeded on your screen.
    IMG_3519
  32. To get hold of the framework, expand the product folder from the left hand side sidebar.
  33. Select the Framework you have just built. Note that it should be black in colour. If you have opted to make a framework for OS X, then you should see that framework listed too, it should be in red colour. The red colour indicates that it has not yet been built.IMG_3524
  34. Control-click on the iOS version of the framework and select Show in Finder.
  35. This will take you directly to the folder containing the framework. Copy paste it to the desktop or to any other location to easily access it when required.
  36. Repeat steps 30 – 34 to build the OS X version of the Framework. Make sure that the target selected is OS X.
  37. Once we have done that, we need to test the framework we just created.
  38. Create a dummy iOS Project for testing.
  39. From the left hand side project navigator make sure that the blue project settings file is selected.
  40. Make sure that the Target is selected within the settings screen.
  41. Under the General tab scroll down to the Embedded Binaries section.
  42. Click on the ‘+’ sign to add a framework.IMG_3523
  43. Click on Add other
  44. Navigate to the folder where you saved the Framework and select it.
  45. Click Open
  46. Select Copy Items if needed
  47. The framework should be added to your project.
  48. In the ViewController.swift file import your Framework: import CustomStack
  49. Replace CustomStack with your frameworks name.
  50. Try to write the code which uses the elements you have packaged within the framework.

Creating Mixed Frameworks (Swift & Objective-C)

The process of creating a mixed library is straightforward. Its almost the same as above with some minor differences.

  1. Follow the steps mentioned above to add your Swift Code.
  2. Add your objective-C files to the project.
  3. While adding the files make sure that the checkbox for the targets is selected appropriately. Screen Shot 2016-08-05 at 1.20.37 PM
  4. Write the code that you wish to write in Objective-C. Of course, if you are including prewritten files then you do not need to do this.
  5. To make the Objective-C code accessible in Swift you need to make the following changes:
    1. In the umbrella header of your framework add the line to import the header
      #import "<FrameworkName>/<HeaderName>.h
    2. Modify the access property located within the target membership of the Objective-C header file. IMG_3527
  6. This should make your Objective-C code accessible to the Swift files.
  7. Test the changes by accessing your Objective-C code in your Swift files within the framework.
  8. Test the changes further by embedding your mixed language framework into a project & then try to access both the Swift as well as Objective-C versions of the code in your new project.
  9. To make your Swift code accessible to Objective-C File make the following changes:
    1. Make sure that your Swift code is compatible with Objective-C. There are 2 ways of doing this. One you can make your Swift class inherit from NSObject. The second way is to use the @objc keyword before your class declaration.
    2. In the Objective-C header file add the line to add the bridging header which is auto generated. You do not need to create your own bridging header.
      #import "<FrameworkName>/<FrameworkName>-Swift.h"

      Replace the word FrameworkName with the name of your Framework.

    3. This should allow you to access your Swift code in your Objective-C header file within the same Framework Project.
  10. This way you can make a single framework which contains code written in both Swift & Objective-C.