DocC documentation in Xcode

What is DocC?

DocC is a built-in documentation rendering tool that allows developers to easily build documentation for their code. Traditionally, developers would create documentation for their code via comments and then separately create documentation files for reference for other developers. DocC combines the 2 steps into a single step, making it easy for developers to write documentation and for others to read that documentation.

Requirements

This functionality is built into Xcode, so no other tool is required. You may need a hosting server incase you wish to host a web based version of the documentation. In this article we will be using Github pages to host our documentation.

Markdown comments

A key component to generate the documentation are comments that you have written. The comments must be formatted in a particular way so that Xcode can read them and use them to build the documentation. I have already covered this in an earlier article though we will have a look at some of those in a bit.

Creating additional documentation resources

The documentation tools generate documentation based on several resources:

  • Markdown comments
  • Type definitions, property declaration, and function declarations
  • available attribute

All these sources combined together provide a lot of information. But we are not just limited to these sources. We can add 2 other kinds of resources to our projects.:

  • Articles
  • Tutorial

Articles allow us to provide a little more context to the documentation. This is where advanced concepts such as functionality, underlying behavior, and things to know are presented to the user. It is possible to add diagrams and pictures to explain the concepts too.

Tutorials on the other hand allow the creator of the code to offer help to anyone who uses the API so that they can learn how to use the different features with the help of step by step instructions.

Both articles and tutorials add to the resources to make the documentation richer and more helpful.


Creating documentation for apps and packages

The process of creating documentation for apps/packages/frameworks is largely similar.

We will be using an example to understand how this goes. I will show only a small snippets of the code/comments here. You can download the completed project at the bottom of the file.

We will take a structured approach towards the design of our documentation.

  • First we will add the availability attributes
  • Second we will put comments for our code
  • Third we will add articles to our code
  • Fourth we will provide tutorials for our code

Adding availability attributes

Adding @available attributes is an important and useful part of the documentation process. It helps other users of your code know thing like which version of the language is required. What’s the minimum OS version that is required, and so on. All this becomes part of the documentation too. Let us look at how we can do this.

The code below represents a type called author. It’s a complete code but it’s missing the availability attributes. In fact, you should also see Xcode report an error for the link where we use Date.now saying that it’s only available from macOS 12 or later and that we should put an availability attribute for the same.

public struct Author {
    public var name         : String    = ""
    public var email        : String    = ""
    public var dateOfBirth  : Date      = Date.now
    public var phone        : String    = ""
    public var photo        : Data?
    public var website      : URL?
}

extension Author : CustomStringConvertible {
    public var description: String {
        let df : DateFormatter  = DateFormatter()
        df.dateStyle            = .medium
        df.timeStyle            = .medium
        
        return """
        Author
        ----------
        Name:       \(self.name)
        Email:      \(self.email)
        Birthday:   \(df.string(from: self.dateOfBirth))
        Phone:      \(self.phone)
        Website:    \(self.website?.description ?? "")
        """
    }
}

extension Author : Equatable {
    public static func ==(lhs : Author, rhs : Author) -> Bool {
        lhs.name == rhs.name && lhs.dateOfBirth == rhs.dateOfBirth
    }
}

Let’s add the attributes and see how it looks. The code show now look like this:

@available(swift 5.0)
@available(iOS 14, macOS 12, *)
public struct Author {
    public var name         : String    = ""
    public var email        : String    = ""
    public var dateOfBirth  : Date      = Date.now
    public var phone        : String    = ""
    public var photo        : Data?
    public var website      : URL?
}

@available(swift 5.0)
@available(iOS 14, macOS 12, *)
extension Author : CustomStringConvertible {
    public var description: String {
        let df : DateFormatter  = DateFormatter()
        df.dateStyle            = .medium
        df.timeStyle            = .medium
        
        return """
        Author
        ----------
        Name:       \(self.name)
        Email:      \(self.email)
        Birthday:   \(df.string(from: self.dateOfBirth))
        Phone:      \(self.phone)
        Website:    \(self.website?.description ?? "")
        """
    }
}

@available(swift 5.0)
@available(iOS 14, macOS 12, *)
extension Author : Equatable {
    public static func ==(lhs : Author, rhs : Author) -> Bool {
        lhs.name == rhs.name && lhs.dateOfBirth == rhs.dateOfBirth
    }
}

Adding comments

Let us continue with our previous example. Next we will add comments to the code. We will go use the markup features we saw in the earlier article. The comments should provide more details about the type. Things like version, copyright, date created, author, tips, contact details. In the case of functions you can have information about arguments and return types too. Let us add that to our code.

//
//  File.swift
//  
//
//  Created by Arun Patwardhan on 04/07/23.
//

import Foundation

/**
 Represents the author of the book.
 
 **Protocols**
 
 Conforms to `CustomStringConvertible` and `Equatable`
 
 - version: 1.0
 - note: The `name` and `dateOfBirth` are deemed to be unique properties.
 - since: iOS 14, macOS 11
 - author: Arun Patwardhan
 - copyright: Amaranthine (c) 2023
 - date: 3rd July 2023
 - requires: Swift 5.x
 - Tip: See the article on creating markup comments [Adding formatted Text to Swift](https://arunpatwardhan.com/2017/11/09/adding-formatted-text-to-swift-in-xcode/)
 
 [arun@amaranthine.co.in](mailto:arun@amaranthine.co.in)
 */
@available(swift 5.0)
@available(iOS 14, macOS 12, *)
public struct Author {
    public var name         : String    = ""
    public var email        : String    = ""
    public var dateOfBirth  : Date      = Date.now
    public var phone        : String    = ""
    public var photo        : Data?
    public var website      : URL?
}

@available(swift 5.0)
@available(iOS 14, macOS 12, *)
extension Author : CustomStringConvertible {
    public var description: String {
        let df : DateFormatter  = DateFormatter()
        df.dateStyle            = .medium
        df.timeStyle            = .medium
        
        return """
        Author
        ----------
        Name:       \(self.name)
        Email:      \(self.email)
        Birthday:   \(df.string(from: self.dateOfBirth))
        Phone:      \(self.phone)
        Website:    \(self.website?.description ?? "")
        """
    }
}

@available(swift 5.0)
@available(iOS 14, macOS 12, *)
extension Author : Equatable {
    public static func ==(lhs : Author, rhs : Author) -> Bool {
        lhs.name == rhs.name && lhs.dateOfBirth == rhs.dateOfBirth
    }
}

Note that we did not add comments to the extensions of the type. Also, it isn’t always necessary to add comments. Sometimes the types are fairly simple and self explanatory.

Creating the Documentation catalog

Now that we have added comments letโ€™s add the DocC documentation.

In fact, you donโ€™t have to do much. Simply select build documentation and it should create it for you with the code, comments, and availability information that is already there.

To create our documentation just select Product > Build Documentation from the menu.

You can see that already a lot of information is available without having to add any documentation.

The documentation we add will just build on this.

Creating Documentation Catalogs

The resources that we need for documentation are added within a documentation catalog. Items like Articles, Tutorial, Sample code, images are all added to the documentation catalog. These items are used to build our documentation. Let us add a documentation catalog to our project.

  1. With our package open select File > New > File from the menu bar.
  2. Choose the Documentation catalog option from the template wizard.
  1. Click Next.
  2. You should see the catalog added to your project.
  1. Select the file called Documentation within the Documentation folder. This is the top level documentation file. We will place information about the package in here.
  2. Rename this file to match the name of our project.
  3. Next add the code shown below to our file. We will examine the different items in a moment.
# ``AmaranthineLibrary``

The types available in ths package are to be used in applications that work with books and collections of books.

## Overview

![Library Types](library)

In this documentation we will look at the different types available. The idea behind these types is to support the creation of apps that work in different libraries. This should allow all kinds of institutions to quickly develop their own solutions for in-house libraries.

### Types

| Type | Description |
| --------- | --------------------------------------- |
| `Genre` | This describes the `Genre` of the book. |
| `Book` | This represents a single book |
| `Author` | This describes the author of the book |
| `Library` | This describes the Library type. |

- <enum:Genre>

## Topics

- <doc:BookInformation>
- <doc:LibraryInformation>
- <doc:Tutorial-Table-of-Contents>

@Small {
MIT License
}
  1. Under the resources folder add any image of your choice. I have an image called “library” and have added that in there.

That’s it for now. Let us look at what we have written.

First up a lot of the formatting you see is similar to markdown style. This means that many thing like headings with a ‘#’, designing tables are already familiar. Let us look at the first line.

# ``AmaranthineLibrary``

Next we have text description giving us information about the framework. This is followed by the heading for Overview.

![Library Types](library)

This line of code adds an image to the documentation. The text in the square brackets is the description followed by the name of the image file in round brackets. This name is the same as the name used while uploading the image in step 8. It is also possible to provide variations of the image. You can provide images with different scales and support for dark mode as long as you follow the correct naming convention.

<image name>~dark<scale>.<file extension>

For example, I could have provided different version of the library image.

library~dark@2x.png
library@2x.png

The system picks the correct one based on the need.

| Type      | Description  |
| --------- | --------------------------------------- |
| `Genre` | This describes the `Genre` of the book. |
| `Book` | This represents a single book |
| `Author` | This describes the author of the book |
| `Library` | This describes the Library type. |

This creates a simple table. A single back tick is used to make the text appear in the code syntax.

- <enum:Genre>

This is another form of a link. This links directly to the Genre type documentation page. There are other ways of creating links to documentation pages. We see them in the code snippet below.

- <doc:BookInformation>
- <doc:LibraryInformation>
- <doc:Tutorial-Table-of-Contents>

Now we haven’t created these articles yet but this is how we would create links to them. The name matches the name of the article itself. It could also be a link to a tutorial page.

@Small {
MIT License
}

Finally this allows us to include any fine print text we wish to add to our page. Build the documentation and look at the output. Notice that we get errors for the links to the articles as we have created them yet. For now we will delete the links to the LibraryInformation and the Tutorial table of contents.

Next we will look at creating articles.

Creating Articles

Articles allow us to provide more information about the different types that we have declared in our code. As I mentioned earlier. It allows us to add more information to the existing documentation that has been built. Let us go ahead and create the article for the Book type.

  1. Click on File > New > File from the menu bar.
  2. Select the document type as Article from the template wizard.
  3. Name it BookInformation
  4. Create it.
  5. Add the following code to the article.
# BookInformation

The ``Book`` type represents a single book.

## Overview

![Book](book)

The type is built up using several different properties. ``Book/author``, ``Book/genre``, ``Book/pageCount``, ``Book/publishedOn``, ``Book/title``, and ``Book/isbn``. Is a ``Book`` is to be uniquely identified then the ``Book/isbn`` property can be used for the same.

### Protocols supported
- `CustomStringConvertible`
- `Equatable`

### Output format

```shell
"E-book"
"Hardbound"
"Paperback"
"Web page"
```
## Topics

### Types

- ``Book``
  1. Add an image called ‘book’ to the resources folder.

Let us have a look at the different things added.

# BookInformation

The ``Book`` type represents a single book.

First up we have the title of the article. Then we have its description. Within the description there is a link for the Book type. Included using the double back ticks. This is a good way to help people reading the documentation to directly go over to the type itself.

## Overview

![Book](book)

The type is built up using several different properties. ``Book/author``, ``Book/genre``, ``Book/pageCount``, ``Book/publishedOn``, ``Book/title``, and ``Book/isbn``. Is a ``Book`` is to be uniquely identified then the ``Book/isbn`` property can be used for the same.

Then we have the overview title with the image of a book. This is followed by a description along with links to properties within the type. Links to such properties are established using The path approach. Where we first mention the Type followed by a slash followed by the property. The rest of the article lists out the protocols that our type conforms to and the output format incase its to be printed.

Similarly we will add an article for the Author. Create a new article called author information. Add the code below.

# ``AmaranthineLibrary/Author``

@Metadata {
@DocumentationExtension(mergeBehavior: override)
}

The ``Author`` type represents the author of the book.

## Overview

![Author](author)

This type is built up using 3 properties: ``Author/name``, ``Author/email``, and ``Author/dateOfBirth``. An instance of ``Author`` is said to be unique if both the ``Author/name`` as well as the ``Author/dateOfBirth`` are unique.

## Output format for description

```shell
Author
----------
Name: ABC
Email: abc@mail.com
Birthday: 23 January 1998
"""
```

## Information
> Note: The ``Author/name`` and ``Author/dateOfBirth`` are deemed to be unique properties.

> Important: Requires Swift 5.x

> Tip: See the article on creating markup comments [Adding formatted Text to Swift](https://arunpatwardhan.com/2017/11/09/adding-formatted-text-to-swift-in-xcode/)

[arun@amaranthine.co.in](mailto:arun@amaranthine.co.in)

## Topics

### Types

``Author``

Most of the items are a the same. Let us look at some new things here.

# ``AmaranthineLibrary/Author``

Notice that the title of the article is now a link to the type within the project rather than a static name.

@Metadata {
@DocumentationExtension(mergeBehavior: override)
}

Next we have the metadata. This tell Xcode how to handle the document creation. Should it merge the auto generated documentation with the contents of our article or should the contents of the article override the information. Here we are saying it overrides.

## Information
> Note: The ``Author/name`` and ``Author/dateOfBirth`` are deemed to be unique properties.

> Important: Requires Swift 5.x

> Tip: See the article on creating markup comments [Adding formatted Text to Swift](https://arunpatwardhan.com/2017/11/09/adding-formatted-text-to-swift-in-xcode/)

[arun@amaranthine.co.in](mailto:arun@amaranthine.co.in)

This bit of code is also different. It creates sections for tips, notes, important information. It renders in the documentation with color highlights.

Build the documentation and see how it looks.

There are many other links and formatting options available.

  • You can control the page layout using tabs, tables.
  • You can add small disclosure text
  • Links can be added to specific properties and functions.
  • Extra top level documents: These are not articles related to a specific type but rather general information about the project.
  • Availability for the documentation
  • Comments: Items that are not rendered but are present for the creator of the documentation to take notes
  • Page appearance

Don’t forget to look at the completed code to see the different kinds of formats that have been used.

Next we will look at creating tutorials.

Creating tutorials

Tutorials as the name suggests are simple guides that walk through the usage of your code. Its a great way to help users of your code to learn how to use the types and functions that you have declared.

Tutorials are easy to create. Lets start of by creating the table of contents file.

  1. Click on File > New > File from the menu bar.
  2. Choose Tutorial Table of Contents.
  3. Give it a name
  4. Create it.
  5. It should come pre-populated with some formatted text to show the table of contents.
  6. Replace that with the code shown below. We will explore the different parts of the text in a moment.
@Tutorials(name: "Using the different types available") {
@Intro(title: "How to use the different types") {
In this tutorial we will look at creating and using the different types.

@Image(source: library.png, alt: "Library")
}

@Volume(name: "Creating types") {

First we will look at how to create instances of the different types.
@Image(source: create.png, alt: "Create")

@Chapter(name: "Author") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Author``.
@Image(source: author.png, alt: "Author")
@TutorialReference(tutorial: "doc:AuthorTutorial")
}

@Chapter(name: "Genre") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Genre``.
@Image(source: genre.png, alt: "Genre")
@TutorialReference(tutorial: "doc:GenreTutorial")
}

@Chapter(name: "Book") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Book``.
@Image(source: book.png, alt: "Book")
@TutorialReference(tutorial: "doc:BookTutorial")
}

@Chapter(name: "Library") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Library``.
@Image(source: library.png, alt: "Library")
@TutorialReference(tutorial: "doc:LibraryTutorial")
}
}

@Volume(name: "Working with the library") {
Next we will look at how all the types work together as a part of the library.

@Image(source: assemble.png, alt: "Assemble")

@Chapter(name: "Working with the library") {
In this chapter we look at how to use the ``AmaranthineLibrary/Library`` object.
@Image(source: library.png, alt: "Library")
@TutorialReference(tutorial: "doc:UsingTheLibraryTutorial")
}
}

@Resources {
Explore more resources for learning about the different features that we have used in Swift.

@Videos(destination: "https://www.youtube.com/channel/UC127UHd8V7bxPQYnd9QrN8w") {
To view various blog articles and videos.

- [My Blog](https://www.arunpatwardhan.com/)
}

@SampleCode(destination: "https://github.com/AmaranthineTech") {
Download and explore sample code projects.

- [Sample code](https://github.com/AmaranthineTech/)
}

@Documentation(destination: "https://amaranthinerandomgenerators.github.io/documentation/amaranthinerandomgenerators/") {
Browse and search documentation for ``AmaranthineLibrary`` project online.

- [AmaranthineLibrary](https://amaranthinelibrary.github.io/documentation/amaranthinelibrary/)
}
}
}

Let us examine each statement block line by line.

@Tutorials(name: "Using the different types available") 

Right at the top we have the title for the tutorial. All the chapter and volume listings are within this block.

@Intro(title: "How to use the different types") {
In this tutorial we will look at creating and using the different types.

@Image(source: library.png, alt: "Library")
}

Then we have the introduction for the tutorial. Here we can give a brief introduction about the tutorial itself. We can add artwork to help illustrate things for the user.

@Volume(name: "Creating types") {

First we will look at how to create instances of the different types.
@Image(source: create.png, alt: "Create")

@Chapter(name: "Author") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Author``.
@Image(source: author.png, alt: "Author")
@TutorialReference(tutorial: "doc:AuthorTutorial")
}

...
}

Next we provide the list of chapters. We can directly provide the list of chapters, or, if our tutorial covers different sections we can have multiple volumes each with a list of chapters. That is what I have done in this example.

The name of the volume, some text explains what is covered in this volume. With the chapter blocks in it. The chapters have a similar structure with name, text, image, and a link to the tutorial document.

@Resources {
Explore more resources for learning about the different features that we have used in Swift.

@Videos(destination: "https://www.youtube.com/channel/UC127UHd8V7bxPQYnd9QrN8w") {
To view various blog articles and videos.

- [My Blog](https://www.arunpatwardhan.com/)
}

@SampleCode(destination: "https://github.com/AmaranthineTech") {
Download and explore sample code projects.

- [Sample code](https://github.com/AmaranthineTech/)
}

@Documentation(destination: "https://amaranthinerandomgenerators.github.io/documentation/amaranthinerandomgenerators/") {
Browse and search documentation for ``AmaranthineLibrary`` project online.

- [AmaranthineLibrary](https://amaranthinelibrary.github.io/documentation/amaranthinelibrary/)
}
}

At the end there is a resources block. This is a great place to put links to other resources that the reader may find useful. These can be categorized to give the reader more information. Here are some of the categories:

  • Documentation
  • Sample code
  • Videos
  • Forums
  • Downloads

Each of these can contain multiple links. Before we build the documentation let us add a tutorial document. In order to do that let us remove the extra volumes and chapters from the table of contents for the moment. This can be added later. Your final code should look like:

@Tutorials(name: "Using the different types available") {
@Intro(title: "How to use the different types") {
In this tutorial we will look at creating and using the different types.

@Image(source: library.png, alt: "Library")
}

@Volume(name: "Creating types") {

First we will look at how to create instances of the different types.
@Image(source: create.png, alt: "Create")

@Chapter(name: "Book") {
In this chapter we look at how to create objects of type ``AmaranthineLibrary/Book``.
@Image(source: book.png, alt: "Book")
@TutorialReference(tutorial: "doc:BookTutorial")
}
}

@Resources {
Explore more resources for learning about the different features that we have used in Swift.

@Videos(destination: "https://www.youtube.com/channel/UC127UHd8V7bxPQYnd9QrN8w") {
To view various blog articles and videos.

- [My Blog](https://www.arunpatwardhan.com/)
}

@SampleCode(destination: "https://github.com/AmaranthineTech") {
Download and explore sample code projects.

- [Sample code](https://github.com/AmaranthineTech/)
}

@Documentation(destination: "https://amaranthinerandomgenerators.github.io/documentation/amaranthinerandomgenerators/") {
Browse and search documentation for ``AmaranthineLibrary`` project online.

- [AmaranthineLibrary](https://amaranthinelibrary.github.io/documentation/amaranthinelibrary/)
}
}
}

Also add the create image to the resources folder. Now we can create our tutorial document.

  1. Click on File > New > File from the menu bar.
  2. Select tutorial file from the template wizard
  3. Name it ‘BookTutorial’
  4. Replace the prefilled text with the markdown shown below
@Tutorial(time: 10) {
@Intro(title: "Creating an instance of Book.") {
We will look at the steps involved in creating an instance of Book.
}

@Section(title: "Create an Book object") {
@ContentAndMedia {
We will look at the steps involved in creating an instance of Book.

@Image(source: book.png, alt: "Book")
}

@Steps {
@Step {
Create the author object

@Code(name: "BookCodeFile.swift", file: BookCodeFile.swift)
}

@Step {
Create the variable that holds the genre.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-1.swift)
}

@Step {
Create the variable that holds the book style.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-2.swift)
}

@Step {
Gather additional book details.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-3.swift)
}

@Step {
Create the variable that holds the book.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-4.swift)
}
}
}
}

Let us explore what is happening in here.

@Tutorial(time: 10) {

We start off by specifying the time estimate for completing the tasks. This is in minutes.

@Intro(title: "Creating an instance of Book.") {
We will look at the steps involved in creating an instance of Book.
}

Next we provide an introduction for this specific tutorial.

@Section(title: "Create an Book object") {

Then we declare the section for this tutorial. The section contains the steps for a specific task.

@ContentAndMedia {
We will look at the steps involved in creating an instance of Book.

@Image(source: book.png, alt: "Book")
}

We provide a little description for the section along with an image using the ContentAndMedia block.

@Steps {
@Step {
Create the author object

@Code(name: "BookCodeFile.swift", file: BookCodeFile.swift)
}

@Step {
Create the variable that holds the genre.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-1.swift)
}

@Step {
Create the variable that holds the book style.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-2.swift)
}

@Step {
Gather additional book details.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-3.swift)
}

@Step {
Create the variable that holds the book.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-4.swift)
}
}

Then we have the steps within the @Steps block. Each step is in its own @Step block. Note the difference between the two. The outer one is @Steps to indicate it holds a series of steps. Inside this is the @Step which represents a single step.

Each step contains the description for that step along with its @Code block. The way the tutorial works is that it walks the reader through a series of tasks that it performs. What is to be done is described in the text and a sample preview for the code is generated through the code file mentioned in the code block.

We will need to upload a series of code files. Each file contains additional code. Listing them in sequence generates the flow. Add the following files to the resources folder of your documentation. Name them BookCodeFile.swift, BookCodeFile-1.swift, BookCodeFile-2.swift, BookCodeFile-3.swift, and BookCodeFile-4.swift.

BookCodeFile.swift

//
//  BookCodeFile.swift
//  
//
//  Created by Arun Patwardhan on 09/08/23.
//

import AmaranthineLibrary

let authorName      : String    = "Arun"
let authorEmail     : String    = "arun@mail.com"
let authorDOB       : Date      = Date(timeIntervalSince1970: 123456789)
let authorPhone     : String    = "9182736450"
let authorLink      : URL       = URL(string: "https://arunpatwardhan.com")

let arun            : Author    = Author(name: authorName,
                                     email: authorEmail,
                                     dateOfBirth: authorDOB,
                                     phone: authorPhone,
                                     photo: nil,
                                     website: authorLink)

BookCodeFile-1.swift

//
//  BookCodeFile.swift
//
//
//  Created by Arun Patwardhan on 09/08/23.
//

import AmaranthineLibrary

let authorName      : String    = "Arun"
let authorEmail     : String    = "arun@mail.com"
let authorDOB       : Date      = Date(timeIntervalSince1970: 123456789)
let authorPhone     : String    = "9182736450"
let authorLink      : URL       = URL(string: "https://arunpatwardhan.com")

let arun            : Author    = Author(name: authorName,
                                     email: authorEmail,
                                     dateOfBirth: authorDOB,
                                     phone: authorPhone,
                                     photo: nil,
                                     website: authorLink)

let bookGenre       : Genre     = Genre.educational

BookCodeFile-2.swift

//
//  BookCodeFile.swift
//
//
//  Created by Arun Patwardhan on 09/08/23.
//

import AmaranthineLibrary

let authorName      : String    = "Arun"
let authorEmail     : String    = "arun@mail.com"
let authorDOB       : Date      = Date(timeIntervalSince1970: 123456789)
let authorPhone     : String    = "9182736450"
let authorLink      : URL       = URL(string: "https://arunpatwardhan.com")

let arun            : Author    = Author(name: authorName,
                                     email: authorEmail,
                                     dateOfBirth: authorDOB,
                                     phone: authorPhone,
                                     photo: nil,
                                     website: authorLink)

let bookGenre       : Genre     = Genre.educational

let bookStyle       : BookStyle = BookStyle.paperback

BookCodeFile-3.swift

//
//  BookCodeFile.swift
//
//
//  Created by Arun Patwardhan on 09/08/23.
//

import AmaranthineLibrary

let authorName      : String    = "Arun"
let authorEmail     : String    = "arun@mail.com"
let authorDOB       : Date      = Date(timeIntervalSince1970: 123456789)
let authorPhone     : String    = "9182736450"
let authorLink      : URL       = URL(string: "https://arunpatwardhan.com")

let arun            : Author    = Author(name: authorName,
                                     email: authorEmail,
                                     dateOfBirth: authorDOB,
                                     phone: authorPhone,
                                     photo: nil,
                                     website: authorLink)

let bookGenre       : Genre     = Genre.educational

let bookStyle       : BookStyle = BookStyle.paperback

let bookTitle       : String    = "Introduction to Swift"
let bookISBN        : String    = "34243-3433-2"
let pageCount       : Int       = 987
let publicationDate : Date      = Date(timeIntervalSince1970: 9876543210)

BookCodeFile-4.swift

//
//  BookCodeFile.swift
//
//  BookCodeFile.swift
//
//
//  Created by Arun Patwardhan on 09/08/23.
//

import AmaranthineLibrary

let authorName      : String    = "Arun"
let authorEmail     : String    = "arun@mail.com"
let authorDOB       : Date      = Date(timeIntervalSince1970: 123456789)
let authorPhone     : String    = "9182736450"
let authorLink      : URL       = URL(string: "https://arunpatwardhan.com")

let arun            : Author    = Author(name: authorName,
                                     email: authorEmail,
                                     dateOfBirth: authorDOB,
                                     phone: authorPhone,
                                     photo: nil,
                                     website: authorLink)

let bookGenre       : Genre     = Genre.educational

let bookStyle       : BookStyle = BookStyle.paperback

let bookTitle       : String    = "Introduction to Swift"
let bookISBN        : String    = "34243-3433-2"
let pageCount       : Int       = 987
let publicationDate : Date      = Date(timeIntervalSince1970: 9876543210)

let swiftTextBook   : Book      = Book(title: bookTitle,
                                       author: arun,
                                       publishedOn: publicationDate,
                                       isbn: bookISBN,
                                       pageCount: pageCount,
                                       genre: bookGenre,
                                       format: bookStyle)

Build the documentation and see how it renders the tutorial. It should look like this:

You can add preview images to your tutorial too to give a visual preview for your code. This is really useful when you are creating tutorials for UI based elements.

Adding assessments

One nice feature of tutorial is the ability to add assessments.

Assessments are a good way of helping readers determine if they have understood specific aspects of the code well. Itโ€™s also a good way to drive home key concepts related to the code.

Assessments are added to the tutorial and is located at the bottom of the tutorial section. Add the following to our Book Tutorial:

@Assessments {
@MultipleChoice {

Which of the following types is not used while creating an instance of a `Book`?

@Choice(isCorrect: false) {
`float`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}

@Choice(isCorrect: false) {
`Data`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}

@Choice(isCorrect: true) {
`Author`

@Justification(reaction: "That's right!") {
A `Book` has an `Author`.
}
}

@Choice(isCorrect: false) {
`Bool`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}
}
}

Let us explore each item in this.

@Assessments {

First we have our assessments block. All our multi choice questions go in here.

@MultipleChoice {

Which of the following types is not used while creating an instance of a `Book`?

Then we have the multi choice block along with the question itself.

@Choice(isCorrect: false) {
`float`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}

A MulitpleChoice block contains 2-4 choices. Each choice is represented with its own @Choice block. A choice block has a boolean flag indicating if its the right answer, the choice value, and a hint in the form of a justification to guide the reader to the correct value in case the choice isn’t correct.

Your complete tutorial should now look like:

@Tutorial(time: 10) {
@Intro(title: "Creating an instance of Book.") {
We will look at the steps involved in creating an instance of Book.
}

@Section(title: "Create an Book object") {
@ContentAndMedia {
We will look at the steps involved in creating an instance of Book.

@Image(source: book.png, alt: "Book")
}

@Steps {
@Step {
Create the author object

@Code(name: "BookCodeFile.swift", file: BookCodeFile.swift)
}

@Step {
Create the variable that holds the genre.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-1.swift)
}

@Step {
Create the variable that holds the book style.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-2.swift)
}

@Step {
Gather additional book details.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-3.swift)
}

@Step {
Create the variable that holds the book.

@Code(name: "BookCodeFile.swift", file: BookCodeFile-4.swift)
}
}
}

@Assessments {
@MultipleChoice {

Which of the following types is not used while creating an instance of a `Book`?

@Choice(isCorrect: false) {
`float`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}

@Choice(isCorrect: false) {
`Data`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}

@Choice(isCorrect: true) {
`Author`

@Justification(reaction: "That's right!") {
A `Book` has an `Author`.
}
}

@Choice(isCorrect: false) {
`Bool`

@Justification(reaction: "Try again!") {
Have a look at the `Book` type to see what has been used.
}
}
}
}
}

Build the documentation. Explore the tutorial and its assessment. It should look like this. The incorrect answers are highlighted in red while the correct one is in green.

That’s it. That covers the basic elements of creating documentation and tutorials for your code. Don’t forget to look at the completed code below.

Top level documentation and other markdown attributes

There are many kinds of attributes available for markdown. We have already seen some of them above. Lets look at a few more.

@MetaData

This attribute allows us to specify how DocC should build this document. Here are some of the items you can mention in there:

AttributeDescriptionPossible values
@DocumentationExtensionUsed to indicate if the contents of the article should override the default documentation or be appended to it.override, append
@PageColorUsed to specify the color to be used for the banner at the top of the pageblue, gray, green, orange, purple, red, yellow
@TechnologyRootUsed to indicate that this is a top level document and that it is not related to any specific type or code in the framework. This is useful when we want to provide some other information not related to the API in question.
@AvailableIndicates the availability of the documentation itself.Platform: iOS, macOS, watchOS, tvOS
@CallToActionThis is used to provide links to resources or downloads associated with that particular page.Purpose argument can have: download, link
@PageKindUsed to specify if the page added is an article or a sample code that is being displayed.article, samplecode
@PageImageUsed to provide an image for the page.Purpose argument can have: icon, card
@DisplayNameUsed to provide a custom name for a page rather than the symbol’s name.String
@SupportedLanguageUsed to specify which programming language supports the specific feature.swift, objc, objective-c

@Options

Similarly we can configure some options for the documentation. This controls how the documentation is rendered. It could be for a specific page or for all the pages in the API. Here are some of the options that we can configure.

AttributeDescriptionpossible values
@AutomaticSeeAlsoUsed to indicate if the see also section is automatically created or not.enabled, disabled
@AutomaticTitleHeadingUsed to indicate if the title head is automatically created or not.enabled, disabled
@TopicsVisualStyleUsed to specify how the topics on a page should be shown.list, compactGrid, detailedGrid, hidden
@AutomaticArticleSubheadingUsed to indicate if the article subheading is automatically created or not.enabled, disabled

@Row

We can add rows and columns too.

@Row {
@Column {
Metadata
}


@Column {
<doc:Author>,
<doc:LibraryInformation>
}
}

@Row {
@Column {
Options
}


@Column {
<doc:AmaranthineLibrary>
}
}

@Row {
@Column {
Tip, Note, Important
}


@Column {
<doc:Author>
}
}

@Row {
@Column {
Tutorials
}


@Column {
<doc:Tutorial-Table-of-Contents>, <doc:AuthorTutorial>, <doc:BookTutorial>, <doc:GenreTutorial>, <doc:UsingTheLibraryTutorial>
}
}

@Row {
@Column {
Assessemnts
}


@Column {
<doc:BookTutorial>
}
}

The above code generates a systematic structure like this:

@TabNavigator

We can offer information on a page with the help of a tab navigator too. This allows us to quickly show multiple options or related information in a structured way.

@TabNavigator {
@Tab("add") {
![add](add)
}

@Tab("assemble") {
![assemble](assemble)
}

@Tab("author") {
![author](author)
}

@Tab("book") {
![book](book)
}

@Tab("checkout") {
![checkout](checkout)
}

@Tab("create") {
![create](create)
}

@Tab("find") {
![find](find)
}

@Tab("genre") {
![genre](genre)
}

@Tab("library") {
![library](library)
}
}

This renders it as:

If there are fewer tabs then it renders slightly differently.

@Links

We can add a list of links too.

@Links(visualStyle: list) {
- <doc:AuthorTutorial>
- <doc:BookTutorial>
- <doc:GenreTutorial>
- <doc:LibraryTutorial>
}

This renders into a simple list of links. You can choose to have it in a compactGrid style or detailedGrid style.

@Small

There is also a way to add small disclaimer or licensing text using the @Small block.

@Small {
MIT License

Copyright (c) 2015 Amaranthine

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
}

This renders it as:

@Comment

Just like we can have comments in our code, we can have comments for our documentation too. The documentation builder does not render them and it is only meant for the author(s) of the documentation. This is a good way to put notes in for things that need to be done.

@Comment {
Dont forget to change the name of this file.
}

Exporting documentation

Now that we have seen different ways of documenting our code its time to start sharing it with our users. Of course when ever users of our package add the package to their project they can simply build the documentation as we have been doing so far. But in some situations users would like to go through the documentation before hand or would like to access it to check something. It is possible to export our documentation to make it accessible to them.

There are a couple of ways of exporting our documentation:

  • Directly export the documentation from the graphical user interface
  • Using the docc command from the command line interface

Let us look at both.

Exporting the documentation via the GUI

  1. First build the documentation for your project.
  2. Select the top level documentation file from the documentation window.
  1. Hover over the right hand side of the documentation name. You should see a more button with 3 dots appear.
  2. Click on the 3 dots and choose “Export”
  1. Choose where you wish to save the archive.
  2. Export it.
  3. Now open the archive by double clicking on the file.
  4. You should see the same documentation but under the imported catalog section.

Export using the command line

  1. First make sure that your project is allready pushed and commited to the github archive.
  2. Now we will be using the Swift-DocC plugin to generate the documentation. We need to add it as a dependency to the Swift Package. Update the Package.swift file to include the dependency.
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AmaranthineLibrary",
    platforms: [
        .iOS(.v14),
        .macOS(.v11),
      ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "AmaranthineLibrary",
            targets: ["AmaranthineLibrary"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "AmaranthineLibrary",
            dependencies: []),
        .testTarget(
            name: "AmaranthineLibraryTests",
            dependencies: ["AmaranthineLibrary"]),
    ]
)
  1. Next we will run the swift command to generate documentation. Run the following command in your package folder.
swift package generate-documentation --source-service github --source-service-base-url https://github.com/AmaranthineTech/AmaranthineLibrary/blob/main --checkout-path /Users/instructor/Developer/AmaranthineLibrary/

Update the paths to match your own implementation. I have cloned the git repository in the /Users/instructor/Developer/ folder.

When you run the command it will tell you where the doccarchive is saved.

  1. Copy the doccarchive and share it.
  2. Open it to view the links to the different files. These links are generated thanks to the --source-service and --source-service-base-url options.

The links to the files should look like this:

This is one of the big advantages of generating the archive via the command line. You could also use the xcodebuild and xcrun to generate the documentation too.


Hosting the documentation

Exporting documentation is one way of sharing the documentation with users. But it would be even better if we could publish it as a webpage. Let us look at how to do that.

There are a couple of ways of publishing the documentation to a website.

  • File server
  • Web server with custom routing
  • Static pages on github

We will look at how to host them as static pages on github.

In order to host static pages on Github you will need a Github account. You can create one for free if you want.

There are 3 broad steps involved in hosting our documentation webpage on Github.

  1. Creating the Github repository for hosting the webpages
  2. Generating the publishable version of our documentation
  3. Uploading the documentation to Github.

Let’s look at those 3 steps in detail.

Step 1: Creating a Github repository for hosting the web pages.

We are going to use a feature called Github Pages. As explained on the website:

GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files straight from a repository on GitHub, optionally runs the files through a build process, and publishes a website.

Github Documentation

There are 3 types of sites that can be hosted:

TypeDescriptionSample URL
ProjectThe site is connected to a project on Github
UserThe site is hosted in a repository owned by a personal user account.<username>.github.io
OrganisationThe site is hosted in a repository owned by an organisation account.<organisation>.github.io

Depending on your needs you can go in for any one of those. For this demo we will be going in for an Organisation site.

The name of the repository will be in the format mentioned in the sample url above. So let us go ahead and create one.

  1. Create an organisation on Github if needed. You can use an existing one if you want.
  2. Next we will create a repository to host our website. Click on repository and create new.
  3. We need to provide the name of our repository. It should follow the format: <organisation name>.github.io .
  4. Provide a description, this is optional.
  5. Set the site as public or private depending on your requirements.
  6. Create the repository.
  7. Navigate to the repository
  8. Go to settings
  9. Select the pages tab.
  10. Make sure “Deploy from a branch” is sected under source.
  11. Under branch select main and the folder as /root.
  12. Your pages screen should look like this:
  1. Go back to your code section of the repository.
  2. Add a new file called readme.md. Put some basic text in it.
  3. Switch back to the settings > pages section of the repository.
  4. You should see a link to the repository.
  1. Click on Visit site. You should see your readme.md file open in your browser. We will be replacing it with our docc documentation.
  2. Let us clone this repository on our computer.
  3. Now we can add our files there. Run the following commands. I will be creating the repository in the ~/Developer folder on my computer.
cd ~/Developer
git clone https://github.com/AmaranthineLibrary/amaranthinelibrary.github.io
cd amaranthinelibrary.github.io
mkdir docs

Step 2: Generating a publishable version of our documentation

Now that our repository is ready we will generate the documentation.

There are couple of ways of generating that documentation. However, we will simply extract it from the archive we created previously.

  1. Copy the archive from the exporting section where we used the Swift-DocC plugin.
  2. Save it within the ~/Developers folder.
  3. View the contents of the archive by control clicking on it and selecting “Show Package Contents”.
  1. Copy the contents of that folder.
  2. Paste them in the website repository we cloned on our computer, within the docs folder.

Step 3: Uploading the documentation and publishing

The next and final step is to upload this to github and its ready. Use the following commands:

cd ~/Developer/amaranthinelibrary.github.io
git add docs
git commit -m "New files"
git push

Thats it. We have uploaded our documentation. Now we will modify our page to ue the docs folder as source.

Go back to github.com and settings for your repository. Update the pages sestion to use the source as docs.

To view it use the following link format:

https://<repository link/documentation/target

Here are the links to the documentation that I created.

https://amaranthinelibrary.github.io/documentation/bloginformation

https://amaranthinelibrary.github.io/documentation/amaranthinelibrary

There are 2 because i added a top level documentation.


Final thoughts

There you go. We have successfully created, viewed, exported, and hosted documentation for our API using Swift DocC.

As you can see the process is fairly simple and straightforward. Yes, it does appear like its a lot of work, but building this practice will go a long way in making your code more useful, and easy to understand for anyone that’s using your code.

The best way to work with DocC is to start writing the comments, availability attributes, articles, and tutorials as you develop your code. This is far better than leaving it as a standalone activity.

So go ahead, use DocC in as many places as possible, even app projects. It will make life very simple.

Download

You can download the Swift Package Manager project here.


Video

Creating Code Snippets in Xcode

What are code snippets?

Code snippets are as the name suggests, short pieces of code that can quickly be inserted into your code file. This is done either by dragging the snippet or by typing out the completion. Code snippets are very easy to create and use and can be applied in a wide variety of situations.

We will look at how you can create & use snippets. The following example is done in a playground, but this could be done from anywhere within Xcode.

Note: The example below was performed on Xcode 11.7

How do we create code snippets?

  1. Start off by writing the code or text that you want to convert into a snippet. For example, I have a set of comments that I add at the start of every function. Write it down.
/**
 This function performs a comparison of the 2 objects
 - important: This function does not perform data validation.
 - returns: `Bool`.
 - requires: iOS 13 or later
 - Since: iOS 13
 - parameter lhsValue: This holds the value on the lhs of the operator
 - parameter rhsValue: This holds the value on the rhs of the operator
 - Example: `var answer =  venueAddress == hotelAddress`
 - author: Arun Patwardhan
 - copyright: Copyright (c) Amaranthine 2020
 - date: 14th September 2020
 - version: 1.0
 */

2. Select it.
3. From the menu bar select Editor > Create Code Snippet.

This brings up the snippet editor.
4. Give your snippet the following details.

OptionDescription
NameThis is the name of your code snippet.
PlatformThis determines whether your snippet is available only for certain platforms: say only for iOS.
AvailabilityThis determines the place where the snippet can be added.
CompletionThis is the word that we will be typing in the Xcode editor to trigger the implementation of the snippet
LanguageThis specifies the language for which the snippet will be applied.

Name: Func Documentation

Language: Swift

Platform: All

Availability: All scopes

Completion: doc

Note that the values for Name and Completion can be whatever you want.

This is how the snippet should look.

5. Now we will try to use it in the editor. Start typing the completion word in the Xcode editor.

6. Select the snippet with your name and completion.
7. Hit enter. You should see the comments you want appearing in the editor.

Placeholder

We can make our snippet above even better by using placeholders. Placeholders are pieces of text that can be replaced by the user. They also give information about what is expected in the placeholder.

We can add place holders by simply typing the hint inside placeholder brackets. Placeholder brackets are nothing but open <# and closing #>. For example:

<# some text #>

Which appears as

The user will simply click on the “some text” placeholder.

There are plenty of places in our comments where we can use placeholders. When we use the code snippet it should put comments with place holders in them.

  1. Let us change the comments in our Xcode editor first. We will edit the snippet later on. Make the changes as shown below.
/**
 <# put the description of your function here #>
 - important: <# mention some important points here #>
 - returns: `<# return type #>`.
 - requires: iOS  <#iOS Version#>  or later
 - Since: iOS  <#iOS Version#>
 - parameter <#param 1#>: This holds the value on the lhs of the operator
 - parameter <#param2#>: This holds the value on the rhs of the operator
 - Example: `<#put some example code here#>`
 - author: Arun Patwardhan
 - copyright: Copyright (c) Amaranthine 2020
 - date: <#day#>  <#month#>  <#year#>
 - version: 1.0
 */

We have made the following items into comments.

  • Description
  • OS Version
  • Return type
  • Important comments
  • Parameter 1 & 2 names
  • Sample code
  • Day, Month, & Year

Of course, there are other things we could change too. Feel free to make any other changes you can think of.

2. Let us now copy these changes to the code snippet we created. Copy the code from the Xcode editor.

To bring the snippet editor again simply click on the add object button in the upper right hand corner of Xcode.

4. Select the snippet from the list on the left and click edit.
5. Paste the code that you just copied. Your snippet editor should look like this:

6. Click on ‘Done’ once you are finished making changes. Your snippet will now be ready.

7. Try adding the snippet into your editor just like before. Simply type in the completion for your snippet.

Dragging snippets

We can use the autocompletion we saw earlier. But it is also possible for us to drag snippets.

Exporting code snippets

Once created it is possible to export/import code snippets too. All the snippets are located in the following folder.

~/Library/Developer/Xcode/UserData/CodeSnippets/

Any snippets you have created will be located there.

Any new snippets to be added will have to be added there.

Summary

Code snippets are easy to create and have several advantages:

  1. They improve the developers experience
  2. Promote consistent code
  3. Speeds up the process of writing code
  4. Encourages developers to use each others snippets and gain the first 3 advantages.

Creating and using snippets is very very easy and has a lot of benefits. So go ahead and create snippets.

Creating custom templates for iOS App Development

What are Xcode templates?

Xcode templates are basically pre-created files which we use when we create new projects or project files. So every time you go through the process of creating a new project File > New > Project > iOS > Single View App you are using the Single View App template.

While most of the templates are good enough we can easily create our own templates.

Why do we need custom templates?

The templates available out of the box are good for common situations. But we find that most of the times we end up creating a lot of file in our project. Sometime we implement common design patterns and architectures on a regular basis.

In such situations creating out own custom templates will help us save a lot of time during development.

The other advantage is that this promotes a more consistent development experience in any organisation.

Now that we know what templates are and why we may need custom templates let us look at how we can create them.

Template Types

Before we go ahead and create templates let us examine what a typical template includes.

Navigate to the following path:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/

Notice that there are 2 folders already created out here. File Templates & Project Templates. Let us browse through these folders.

File Templates

These are the templates used when a developer wishes to add a new file to an existing project. Under file templates you should see different folders in there. Each folder represents a certain category of templates. For example, User Interface is one category. Select it.

You should see multiple folders under it. The screenshot above shows the View template. As we can see the template itself is a folder with multiple files inside. The template ends with an extensions xctemplate. Let us look at those files.

  • ___FILEBASENAME___.xib
  • TemplateIcon.png
  • TemplateIcon@2x.png
  • TemplateInfo.plist

The first one is the XIB file which will be generated by this template. The ___FILEBASENAME___ placeholder will be replaced with an actual name when it is created.

The next 2 are simply images that will be used as icons for the template when we bring up the template wizard in Xcode.

The last one is the more important one. The TemplateInfo.plist. This is where we describe how the file creation process works. This is also where we configure options which will be presented to the user. We will look at this file in greater depth later on when we try to create our own templates.

Project Templates

These are the templates that are used when a developer decides to create a new project. Under project templates you should see different folders in there. Each folder represents a certain category of templates. For example, Application is one category. Select it.

I have the single view app template inside it. This is the most commonly used template when starting out with iOS App Development. You should see other familiar project templates. Feel free to examine the files in the folder. Let us have a look inside the Single View App template folder. You should see these items:

  • ContentView.swift
  • Main.storyboard
  • TemplateIcon.png
  • TemplateIcon@2x.png
  • Preview Assets.xcassets folder
  • TemplateInfo.plist

The first 2 files are the UI related files. One of the 2 will be selected based on the users choice between Storyboard and SwiftUI.

The next 2 are simply images that will be used as icons for the template when we bring up the template wizard in Xcode.

The Preview Assets folder is used with SwiftUI for previewing purposes.

Here too we have the TemplateInfo.plist file which configures the template options at the time of creation. We will explore this file in greater depth when we try to create our own project template.

How can we create them?

In this article we will look at creating 2 types of templates.

  1. File Templates
  2. Project Templates

Warning: It may be a good idea to try this out on a test computer so that you do not break anything on the computer you use everyday.

Preparation

Before we get started let us prepare the folders where we will be storing our custom templates.

  1. Navigate to the following folder.
~/Library/Developer/Xcode/Templates/

Note, you may have to create this folder.

  1. There should be 2 folders inside: File Templates, Project Templates. If these folders are not there then go ahead and create them.

We will be placing our templates in these folders.


TopicPage
Creating File templates2
Creating Project templates3

Download

You can download the templates from these links.

Note

This code has been tested on Xcode 11.3.1 on macOS Catalina 10.15.3

Creating reusable UI Components for iOS App Development

In an earlier article I had discussed how we can create our own frameworks to easily share reusable code. In this article we will take this a little further and create our own reusable UI Components.

Points to Note:

  • The reusable component we will be creating is based on UIKit. For that reason this component can only be used in iOS Apps. However, you can follow the same steps to create a reusable component for macOS using Cocoa.
  • UI Components, distributed through a framework, do not render in the project storyboard file.
  • You should be familiar with creating Embedded Binaries (your own Framework). If you aren’t then please read this article first.
  • These projects are created on Xcode 10 with Swift 4.2

Getting Started

We will complete the implementation as a 2 step process. Here is the screen that we are planning to implement.

Creating the Reusable Framework

  1. Open Xcode and create a new Framework project.
    Screen Shot 2018-09-06 at 1.21.04 PM
  2. Name the project “UIVIdentityCard”.
    Screen Shot 2018-09-06 at 1.23.05 PM
  3. Save the project in any folder.
  4. Create a new Swift file File > New > File > Swift.
  5. Name the file “GenderType.swift”. This is where we will declare the enum that holds the Gender type that we are creating.
  6. Add the following code to the file.
     
    import Foundation 
    /** Possible scores that can be given. 
    *values* 
    `Male` 
    
    `Female` 
    
    `NotSpecified` 
    
    *functions* 
    `func toString() -> String` 
    Used to get the `String` version of the value 
    
    - Author: Arun Patwardhan 
    - Version: 1.0 
    */ 
    public enum GenderType 
    {      
         case Male      
         case Female      
         case NotSpecified 
    } 
    
    /** This extension adds the Enum to String converions capability 
    - Author: Arun Patwardhan 
    - Version: 1.1 
    */ 
    extension GenderType 
    {      
         /** This function converts from enum value to `String`         
         - important: This function does not do validation         
         - returns: `String`.         
         - requires: iOS 11 or later         
         - Since: iOS 11         
         - author: Arun Patwardhan         
         - copyright: Copyright (c) Amaranthine 2015         
         - version: 1.0 */      
         @available(iOS, introduced: 11.0, message: "convert to String")      
         func toString() -> String      
         {           
              switch self           
              {                
                   case .Male:                     
                        return "Male"                
                   case .Female:                     
                        return "Female"                
                   case .NotSpecified:                     
                        return "Not Specified"           
              }      
         } 
    } 
  7. Create a new Swift file called “PersonDetailsModel.swift”.
  8. Add the following code to the file.
    import Foundation
    
    /**  This struct represents the data that is to be shown in the ID card  
    **Variables**  
    `personName`  
    
    `personIcon`  
    
    `personDob`  
    Date of Birth  
    
    `personAddress` 
     
    `personPhone` 
     
    `personEmail` 
     
    `personCompany`
      
    `personHeight`
      
    `personWeight`  
    
    `personGender`  
    
    **Important**  There is a variable with the name `entryCount`. This variable keeps tracks of the number of stored properties that exist. The value of this variable will be used to determine the number of rows in the table.The computed property `numberOfRows` is the property used to access the value of `entryCount`.  
    
    - Author: Arun Patwardhan  
    - Version: 1.0
    */
    public struct PersonDetailsModel
    {     
         internal var entryCount : Int = 7     
         public var personName   : String = ""     
         public var personIcon   : UIImage     
         public var personDob    : Date     
         public var personAddress: String = ""     
         public var personPhone  : String = ""     
         public var personEmail  : String = ""     
         public var personCompany: String = ""     
         public var personHeight : Double? = 0.0     
         {          
              willSet          
              {               
                   if newValue == nil & personHeight != nil               
                   {                    
                        entryCount -= 1               
                   }               
                   else if newValue != nil & personHeight == nil               
                   {                    
                        entryCount += 1               
                   }          
              }     
         }     
    
         public var personWeight : Double? = 0.0     
         {          
              willSet(newValue)          
              {               
                   if newValue == nil & personWeight != nil               
                   {                    
                        entryCount -= 1               
                   }               
                   else if newValue != nil & personWeight == nil               
                   {                    
                        entryCount += 1               
                   }          
              }     
         }     
    
         public var personGender : GenderType?     
         {          
              willSet          
              {               
                   if newValue == nil & personGender != nil               
                   {                    
                        entryCount -= 1               
                   }               
                   else if newValue != nil & personGender == nil               
                   {                    
                        entryCount += 1               
                   }          
              }     
         }     
    
         public var numberOfRows : Int     
         {          
              return entryCount     
         }     
    
         public init(withName newName : String, icon newIcon : UIImage, birthday newDob : Date, address newAddress : String, phone newPhone : String, email newEmail : String, Company newCompany : String, height newHeight : Double?, weight newWeight : Double?, andGender newGender : GenderType?)     
         {          
              personName = newName          
              personIcon = newIcon          
              personDob  = newDob          
              personAddress = newAddress          
              personPhone = newPhone          
              personEmail = newEmail          
              personCompany = newCompany  
            
              if newGender != nil          
              {               
                   entryCount += 1          
              }          
              if newWeight != nil          
              {               
                   entryCount += 1          
              }          
              if newHeight != nil          
              {               
                   entryCount += 1          
              }          
    
              personHeight = newHeight          
              personWeight = newWeight          
              personGender = newGender     
         }
    }
    
    /**     This extension adds protocol conformance for the `CustomStringConvertible` protocol.     
    
    - Author: Arun Patwardhan     
    - Version: 1.1
    */
    extension PersonDetailsModel : CustomStringConvertible
    {     
         public var description: String     
         {          
              return """               
                   NAME: \(self.personName)               
                   DATE OF BIRTH:\(self.personDob)               
                   ADDRESS: \(self.personAddress)               
                   EMAIL:\(self.personEmail)               
                   PHONE:\(self.personPhone)          
              """     
         }
    }
  9. Now we will focus out attention on the View. Create a new file File > New > File > View.
    Screen Shot 2018-09-06 at 2.18.32 PM
  10. Name the view “UIVIdentityCard.swift”.
  11. Design the view as shown in the screenshot below.
    Screen Shot 2018-09-07 at 12.22.49 PM
  12. Create the corresponding“UIVIdentityCard.swift” file.
  13. Make the IBOutlet & IBAction connections for the different UI elements.
  14. Add the following code. This is how your file should look after its completed.
    /**     The UIVIdentityCard class     
    **Functions**     
    `public func load(data newPerson : PersonDetailsModel)`     
    Used to load the data for the view.     
    
    - Author: Arun Patwardhan     
    - Version: 1.0
    */
    @IBDesignableopen class UIVIdentityCard: UIView, UITableViewDelegate, UITableViewDataSource
    {     
         //IBOutlets --------------------------------------------------     
         @IBOutlet public weak var personIcon : UIImageView!     
         @IBOutlet public weak var personName : UILabel!     
         @IBOutlet public weak var personDetails : UITableView! 
    
         //Variables --------------------------------------------------     
         public var localTableData : PersonDetailsModel!     
         let nibName : String = "UIVIdentityCard"     
         var view: UIView!     
         let cellIdentifier : String = "IDCard"     
         //Functions --------------------------------------------------     
         /**     This function does the initial setup of the view. There are multiple things happening in this file.     
         1) The first thing that we do is to load the Nib file using the `nibName` we saved above. The UNIb object contains all the elements we have within the Nib file. The UINib object loads the object graph in memory but does not unarchive them. To unarchive them and get the ibjects loaded completely for use we have to instatiate the object and get the arry of top level objects. We are however interested in the first object that is there in the array which is of type `UIView`. The reference to this view is assigned to our local `view` variable.     
         2) Next we specify the bounds of our view     
         3) Finally we add this view as a subview     
    
         - important: This function does not do validation     
         - requires: iOS 11 or later, the varibale that contains the name of the nib file.     
         - Since: iOS 11     
         - author: Arun Patwardhan     
         - copyright: Copyright (c) Amaranthine 2015     
         - version: 1.0     
         */     
         @available(iOS, introduced: 11.0, message: "setup view")     
         func setup()     
         {          
              //1)          
              self.view = UINib(nibName: self.nibName, bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil)[0] as! UIView          
         
              //2)          
              self.view.frame = bounds          
              
              //3)          
              self.addSubview(self.view)     
         }     
    
         public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int     
         {          
              if let count = localTableData?.entryCount          
              {               
                   return count - 2          
              }          
              return 0     
         }     
    
         public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&amp;gt; UITableViewCell     
         {          
              var cell : UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)          
    
              if nil == cell          
              {               
                   cell = UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)          
              }          
    
              switch indexPath.row          
              {               
                   case 0:                    
                        let formatter = DateFormatter()                    
                        formatter.dateStyle = .medium                         
                        cell?.textLabel?.text = "Birthday\t: "+ formatter.string(from: (localTableData?.personDob)!)      
    
                   case 1:                    
                        cell?.textLabel?.text = "Email\t: " + localTableData.personEmail               
    
                   case 2:                    
                        cell?.textLabel?.text = "Phone\t: " + localTableData.personPhone               
    
                   case 3:                    
                        cell?.textLabel?.text = "Address\t: " + localTableData.personAddress               
    
                   case 4:                    cell?.textLabel?.text = "Company\t: " + localTableData.personCompany               
    
                   case 5:                    
                        cell?.textLabel?.text = "Gender\t: " + \(localTableData.personGender?.toString())!               
    
                   case 6:                    
                        cell?.textLabel?.text = "Height\t: \((localTableData.personHeight)!)"               
    
                   case 7:                    
                        cell?.textLabel?.text = "Weight\t: \((localTableData.personWeight)!)"               
                   default:                    
                        print("error")          
              }          
    
              cell?.textLabel?.font = UIFont.boldSystemFont(ofSize: 12.0)          
              cell?.textLabel?.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)          
              return cell!     
         }     
    
         //Inits --------------------------------------------------                    
         override public init(frame: CGRect)     
         {          
              super.init(frame: frame)          
              self.setup()     
         }     
         
         required public init?(coder aDecoder: NSCoder)     
         {          
              super.init(coder: aDecoder)          
              self.setup()     
         }     
    
         override open func layoutSubviews()     
         {          
         super.layoutSubviews()     
         }
    }
    
    /**     This extension adds the function to load data     
    - Author: Arun Patwardhan     
    - Version: 1.1
    */
    extension UIVIdentityCard
    {     
         /**          
         This function loads the data for the view          
         - important: This function does not do validation          
         - parameter newPerson: This is the object representing the person whose information will be displayed on the screen.          
         - requires: iOS 11 or later          
         - Since: iOS 11          
         - author: Arun Patwardhan          
         - copyright: Copyright (c) Amaranthine 2015          
         - version: 1.0     
         */    
         @available(iOS, introduced: 11.0, message: "load data")     
         public func load(data newPerson : PersonDetailsModel)     
         {          
              self.localTableData = newPerson          
              self.personIcon.image = localTableData.personIcon          
              self.personName.text = localTableData.personName          
              self.personDetails.reloadData()     
         }
    }
  15. Add the placeholder image for the image view.
  16. Select any of the simulators from the list.
  17. Press โŒ˜ + B to build the project.
  18. From the Project navigator select the Framework file.
    Screen Shot 2018-09-07 at 12.29.44 PM
  19. Control click and select “Show in Finder”.
  20. Copy the framework to the “Desktop”.

We are done creating the reusable framework. We will not shift our focus towards testing this framework.

Using the Framework in a project

Let us now test the framework we created. We will do this by incorporating the code in our iOS App.

  1. Create a new project. Call it “IdentityCardTest”.
    Screen Shot 2018-09-07 at 12.33.52 PM
    Screen Shot 2018-09-07 at 12.33.49 PM
  2. Save the file in a folder of your choice.
  3. Select the Project file and Embed the Framework into your project. 
    Screen Shot 2018-09-07 at 12.36.14 PM
  4. Add an image to your project, this will be the image that will be displayed in your custom view.
  5. Switch to the Main.storyboard file. Drag a UIView into the ViewControllers view.
  6. Set its identity to the UIVIdentityCard in the identity inspector. Also set its module to UIVIdentityCard.
    Screen Shot 2018-09-07 at 12.38.11 PM
  7. Create an IBOutlet for this custom view.
  8. Switch to the ViewController.swift file. Import the UIVIdentityCard framework at the top of the file.
    Screen Shot 2018-09-07 at 12.41.13 PM
  9. Add the following code to the file. We will be creating test data and displaying it on the screen using the Custom view we just designed.
    //Functions --------------------------------------------------
    /**
        This function prepares and loads the data that is to be shown in the custom view
    
    
        - important: This function does not do validation
        - requires: iOS 11 or later, the UIVIdentityCard framework.
        - Since: iOS 11
        - author: Arun Patwardhan
        - copyright: Copyright (c) Amaranthine 2015
        - version: 1.0
    */
    @available(iOS, introduced: 11.0, message: "prepares data to be shown on the ID card")
    func prepareIDCard()
    {
         let displayData : PersonDetailsModel = PersonDetailsModel(withName: "Arun Patwardhan", icon: UIImage(named: "iconHolder.png")!, birthday: Date(timeIntervalSince1970: 44_97_12_000), address: "Mumbai, Maharashtra, India", phone: "91-22-26486461", email: "arun@amaranthine.co.in", Company: "Amaranthine", height: 5.11, weight: nil, andGender: GenderType.Male)
    
         myIDCard.load(data: displayData)
    }
  10. Your completed ViewController.swift should look like this.
    Screen Shot 2018-09-07 at 12.44.32 PM
  11. Run the project. See if the view loads the way we wish.

Link to Sample Code

https://github.com/AmaranthineTech/ReusuableUIFramework

Video

Creating Reusable UI

Migrating to Swift from Objective-C

This article explores some of the advantages and challenges faced by developers while migrating to Swift from Objective-C.

1. Do we want to migrate?

Before you start the migration process remove the old adage:

If it isn’t broken, don’t fix it!

Start by identifying the reasons why you wish to migrate. Here are some possible reasons why.

  • The code is old and not updated for a very long time. You now wish to add new features.
  • The frameworks/libraries you are using in your project have upgraded to modern Swift and no longer support your old Objective-C syntax. *You may still want to just update to modern Objective-C, but this would be a good time to jump onto swift.
  • You see potential for improvement in code size/speed/performance by using new Swift features not available in Objective-C. For example: Generic Programming.
  • The developers who developed the app in Objective-C have left and the new employees are proficient at Swift. *Again not a strong reason, but a valid reason if there is no other alternative. Asking people to sit down and learn Objective-C may not be practical,ย especially if they don’t have a background in C Programming.
  • The app is due for a performance, stability, & bug fix update. This is a good time to consider migration to Swift.

Factors to keep in mind before considering migration.

  • The cost of migration. This is the cost of keeping a certain number of developers occupied in migrating the code. The cost is in terms of time as well as money.
  • Potential risks. Any change to the code increases the risk of bugs. The chances of introducing limits on backward compatibility also increase.
  • Benefits gained. An assessment needs to be done as to whether there are any benefits of migrating to Swift. The Return on Investment needs to be figured out.
  • Compatibility with 3rd Party or in house libraries that you might use.

After having thought through all this you are ready for the next step: “Prepare to Migrate”

2. Preparing to Migrate

This is where you actually begin to work on the migration of the App.

  1. As a first step perform a full code review of the app.
  2. The next step is a major decision. Should you rebuild your entire app from scratch or do a piece by piece migration. We will explore the advantages a little later in the article.
  3. Look for Swift versions of 3rd frameworks/libraries you use. This is not strictly required, however, this is a good time to check for new APIs.
  4. Identify parts of the project to migrate. This is to be done if it is a piecemeal migration. This marks you as ready for the next step: “Starting the Migration”.

3. Starting the Migration

Once you have everything in place you are ready to begin.

Migrations happen class by class. Select an Objective-C class to migrate and start working on converting it to Swift.

If you have any pure C functions then you can either choose to make them work with Swift or rewrite them in Swift.

While migrating pay special attention to your code. Here are some conversions that you can make.

  • See if you can make it simpler by using Generic Programming instead of usingVoid *
  • Replace the use of NSError * with exceptions.
  • Use extensions to give types new capabilities.
  • Consider creating your own Data structures. You may use Swift Arrays, Dictionaries if you wish. But this might be a good time to improve performance by building your own data structures.
  • Embrace closures and protocols a lot more.
  • Make extensive use of the @available attribute to describe your changes and mark availability
  • Start incorporating Swift Markup to make the comments from your Objective-C code more readable.
  • Enums pulled in from Objective-C can be made more powerful in Swift by adding methods which work with enums as a part of the enum itself.
  • Use property observers to make code more reactive. In some situations this might be easier than setting observers.

Migration Steps

Here are some general steps you can follow. The steps below are for both a full app conversion or a piece meal conversion.

Note: The steps mentioned below are sample steps and not necessarily the only way to achieve this.
  1. If its a full app conversion then create a new project. Else duplicate the existing project.
  2. Start by looking for the frameworks you need and importing them in the necessary Swift files.
  3. Identify class(es) that you have in your Objective-C project. Start by creating empty versions of those in your Swift project.ย It is very likely that you may not need all the classes as you might be optimising or reworking your App’s architecture. Also it is possible that you may need new classes.
  4. Next identify data structures used in the class. Either convert them to their swift equivalents or explore other options.
  5. Migrate the functions directly associated with the data structures.
  6. Migrate the variables used in the Objective-C class.
  7. Lastly migrate the remaining functions to Swift.
  8. Do this till you have converted all the classes that you wish to convert.

One point left to talk about is testing. Thoroughly test you app after each step you complete. If you are using XCTests, migrate a single Unit test at a time. Corresponding to the changes that you have made above.

5. Things to watch out for

There are many things to keep in mind while migrating your code.

  • In a mixed language project (Swift and Objective-C) Swift only features won’t be supported. So Generic Programming cannot be implemented.
  • Blind copying of the code from Objective-C to Swift may not result in the best output. Try to examine each line for potential optimisation opportunities.
  • Watch out for OS version compatibility. You may have to choose your Swift version accordingly.

6. Full Conversion versus Part by Part Conversion

Full Conversion

PROS:

  • The advantage of building the app from scratch is that your overall development time is less as different parts of the app can be refactored at development time.
  • You also have the advantage of adopting new development approaches or architectures such as Model View View Model (MVVM) or Test Driven Development (TDD).
  • You are in a better position to take advantage of all the Swift features as there won’t be any challenges with compatibility.
  • The advantages of Swift viz: Speed, Safety, and compact code are more easily achieved
  • If you want to support older versions of iOS then having a pure Swift and pure Objective-C version helps.

CONS:

  • Of course this means that your development time is large.
  • There is a potential for writing duplicate code in Swift especially if it is being reused in Objective-C projects. You may end up with 2 code bases for the same feature.

Part Conversion

PROS:

  • The advantage of migrating parts of your app is that you can split the migration over a larger period and use your resources on other projects.
  • In terms of cost this is less expensive and more resource friendly
  • The potential for duplicate code is reduced

CONS:

  • But on the flip side every time you take a new part to migrate you will have to make changes to the Swift code written earlier. This increases the development time and may affect the quality of the app in the long run.
  • You cannot take advantages of all the Swift features.
  • There is a chance that once the migration is complete the App may have to undergo an overhaul to take advantage of the Swift features & improve on Speed, Safety & Size.

This article just talks about some of the advantages and challenges with Migration to Swift. There are multiple approaches available and you will have to pick and choose the approach based on your needs or situation. I had written an article some time back about choosing between Swift & Objective-C, you can have a look at that too. Here is an article, for your reference, written by Apple on Migrating to Swift. Good luck & Happy Programming! Do feel free to share your experience migrating to Swift.

 

Using Swift Package Manager

About Swift Package Manager

The Swift Package Manager is the tool used to build Applications and Libraries. it streamlines the process of managing multiple Modules & Packages. Before we go ahead and learn to use Swift Package Manager we need to get familiar with some basic terminology.

Modules

Modules are used to specify a namespace and used to control access to that particular piece of code. Everything in Swift is organised as a module. An entire app can fit into a module or an app can be made using multiple modules. The fact that we can build modules using other modules means that reusing code becomes a lot easier. So, when we make an iOS App with Xcode and Swift. The entire app is considered a single module.

Targets

Targets are the end product that we want to make. So an app for iOS is a separate target. A library is a target. An app for macOS is a separate target. You can have many targets. Some can be for testing purposes only.

Packages

Packages group the necessary source files together. A package can contain more than one target. Normally one would create a package for a family of products. For example: you want to make a photo editing app that runs on macOS & iOS. You would create one package for it. That package would have 2 targets: an iOS App & a macOS App.

Products

This is a categorisation of your packages. There are 2 types of products. Executables or Libraries. A library contains the module which can be reused elsewhere. Executables are application that run & may make use of other modules.

Dependencies

Dependencies are the modules or the pieces of code that are required to make the different targets within the package. These are normally provided as URLs.

End Products

*NOTE:ย Before you get started you must be familiar withย Setting up Swift on Linux. If you haven’t done that then please go through the updated article:ย UPDATE: Swift on Linux. This also makes use of Swift Package Manager.

Example

So let us get started with an example. We are going to learn how to create:

  • a library package called ErrorTypes
  • a library package, called MathOperations,ย that uses the ErrorTypes library package
  • an executable package called Calcย that makes use of theย MathOperations package.

We will see how to create all three elements. Also I have uploaded theย ErrorTypes & MathOperations packages to the http://www.github.com repository to demonstrate the use of dependencies. You can also create your own local git repositories if you wish.

To illustrate the folder hierarchy: I have created a folder called “Developer” in my Ubuntu linux home folder. Within that I have created a folder called “SPMDEMO“. All the paths that I will be using will be with reference to these folders. You should see a structure like this:

/home/admin/Developer/SPMDEMO/ErrorTypes
/home/admin/Developer/SPMDEMO/MathOperations
/home/admin/Developer/SPMDEMO/Calc

You are free to follow this exercise using your own folder locations. Just modify the paths accordingly.

swift package init
swift package init --type executable
swift build

If you need help with the commands run:

swift package --help
swift --help

Creating a Package

  1. First let us start off by creating theย ErrorTypes package.
    mkdir ErrorTypes
  2. Navigate to the folder and create the package:
  3. cd ErrorTypes
    swift package init
    

    By default init will create a library package type.

  4. Navigate to the folder containing the source files:
    cd ./Sources/ErrorTypes/
  5. Open theย ErrorTypes.swift file and write the following code
    public enum ErrorCodes : Error
    {
         case FileNotFound(String)
         case DivideByZero(String)
         case UnknownError(String)
    }
    
    public struct MathConstants
    {
         static let pi : Float = 3.14159
         static let e ย : Float = 2.68791
    }
    

    Feel free to add some code of your own. The above is just an example.

  6. Run the command to build to make sure that there aren’t any issues. You shouldn’t have any as there are no dependencies of any kind. Its a simple straightforward piece of code.
    swift build
  7. If everything is fine check your code into a git repository. This can be local or on the web. Remember that we will need the URL to this repository.
  8. Navigate back to the SPMDEMO folder.
    cd ~/Developer/SPMDEMO/
  9. Create a folder calledย MathOperations.
    mkdir MathOperations
  10. Navigate to the newly created folder and run the command to create a library package.
    cd MathOperations
    swift package init
    
  11. Navigate to the sources folder:
    cd ./Sources/MathOperations/
  12. Open theย MathOperations.swift file and write the following code.
    import ErrorTypes
    
    public struct MathOperations
    {
         public static func add(Number num1 : Int, with num2 : Int) -> Int
         {
              return num1 + num2
         }
    
         public static func mult(Number num1 : Int, with num2 : Int) -> Int
         {
              return num1 * num2
         }
    
         public static func div(Number num1 : Int, by num2 : Int) throws -> Int
         {
              guard num2 > 0
              else
              {
              throw ErrorCodes.DivideByZero("You are dividing by zero. The second argument is incorrect.")
              }
    
              return num1 / num2
         }
    
         public static func sub(_ num1 : Int, from num2 : Int) -> Int
         {
              return num2 - num1
         }
    }
    
  13. Before we build we need to modify theย Packages.swift file to indicate there is a dependency.
    Notice that in the MathOperations.swift file we are importing a module called ErrorTypes. We just created it. But just because we created it doesn’t mean it will be added automatically. We need to pull that module into our own

    Also notice that I have provided access specifiersย “public” everywhere. This ensures that the code written in one module is accessible in the other.

    Navigate to the MathOperations parent folder.

    cd ~/Developer/SPMDEMO/MathOperations/
  14. Open theย Packages.swift file and make the changes as shown below:
    // swift-tools-version:4.0
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    
    import PackageDescription
    
    let package = Package(name: "MathOperations",
         products: [
              // Products define the executables and libraries produced by a package, and make them visible to other packages.
              .library(name: "MathOperations", targets: ["MathOperations"]),
         ],
    
         dependencies: [
              // Dependencies declare other packages that this package depends on.
              .package(url:"https://github.com/AmaranthineTech/ErrorTypes.git", from:"1.0.0"),
         ],
    
         targets: [
              // Targets are the basic building blocks of a package. A target can define a module or a test suite.
              // Targets can depend on other targets in this package, and on products in packages which this package depends on.
              .target(name: "MathOperations", dependencies: ["ErrorTypes"]),
              .testTarget(name: "MathOperationsTests", dependencies:   ["MathOperations"]),]
    )
    
  15. Once these changes are made save the file and run the command
    swift build

    If you typed everything correctly then you should see the source code for the ErrorTypes module being pulled in and the build being successful.Here are some common mistakes:
    – Forgetting to write the import ErrorTypes statement
    – Error in the URL
    – The from tag not matching the tag in the repository
    – Access specifiers are incorrect or missing
    – Not mentioning the dependencies in the target

  16. Just like with the ErrorTypes module create a git repository for the MathOperations module.
  17. Now let us make theย Calc executable that will use theย MathOperations library. First navigate back to the SPMDEMO folder and create a folder calledย Calc.
    cd ~/Developer/SPMDEMO/
    mkdir Calc
    
  18. This time we are going to create an executable package. Run the command:
    swift package init --type executable

    This also creates a similar folder structure as in the case of the library.

  19. Navigate to the folder containing theย main.swift file.
    cd ./Sources/Calc/
  20. Modify theย main.swift file as shown below:
    import MathOperations
    
    //testing addition
    var result : Int = MathOperations.add(Number: 33, with: 29)
    print("Result of adding 33 with 29 is: \(result)")
    
    //testing multiplication
    result = MathOperations.mult(Number: 33, with: 29)
    print("Result of multiplying 33 with 29 is: \(result)")
    
    //testing division
    do
    {
         result = try MathOperations.div(Number: 33, by: 0)
         print("Result of dividing 33 by 29 is: \(result)")
    }
    catch let error
    {
         print("ERROR: \(error)")
    }
    
    //testing subtraction
    result = MathOperations.sub(3, from: 29)print("Result of subtracting 3 from 29 is: \(result)")
    
  21. Navigate back to the mainย Calc folder.
    cd ~/Developer/SPMDEMO/Calc/
  22. Modify theย Packages.swift file as shown below:
    // swift-tools-version:4.0
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    
    import PackageDescription
    
    let package = Package(name: "Calc",
    dependencies: [
         // Dependencies declare other packages that this package depends on.
         .package(url: "https://github.com/AmaranthineTech/MathOperations.git", from: "1.0.1"),
    ],
    targets: [
         // Targets are the basic building blocks of a package. A target can define a module or a test suite.
         // Targets can depend on other targets in this package, and on products in packages which this package depends on.
         .target(name: "Calc", dependencies: ["MathOperations"]),
    ]
    )
    
  23. Save the file and run the build command:
    swift build
  24. Like before you should see both theย MathOperations &ย ErrorType module being pulled in. We are ready to run the executable. Navigate to the debug folder which contains the executable. Make sure you are in the mainย Calc folder when you run this command.
    cd ./build/debug/
  25. You should see an executable file calledย Calc. Run it.
    ./Calc
  26. If everything went okay then you should see the output on the console.

As you can see it is pretty straightforward to develop Applications written in Swift on Linux.

Adding System Modules

In the previous example we saw how to import our own custom made modules. However, there are some modules provided by the system which offers functionality we may wish to use. For example if we wanted to use the random number generator in our application we would need to use the random() method. This is in the glib module.

  1. Quickly create a package calledย SystemLibs. This is an executable.
  2. Write the following code in theย main.swift.
    #if os(Linux)
    import Glibc
    #else
    import Darwin.C
    #endif
    extension Int
    {
         func toString() -> String
         {
              return "\(self)"
         }
    }
    
    var luckyNumber : Int = Int(random())
    
    var luckyNumberStr : String = luckyNumber.toString()
    
    print("The lucky number is \(luckyNumberStr)")
    
  3. Build the code and run the executable.

Adding system modules is direct and simple. Theย glibc module contains aspects of the standard library. The condition check is to make sure that we are importing the correct module based on the system that we are developing the application on.

Handling Sub-dependencies

As we saw in the earlier example, sub dependencies are handled automatically. So when ourย Calc application marked theย MathOperations module as a dependency it was pulled during the build. However, theย MathOperations module itself markedย ErrorTypes module as a dependency. We did not have to modify theย Packages.swift file belonging toย Calc to indicate thatย ErrorTypes module also needs to be pulled. This was handled automatically by Swift Package Manager.

Conclusion

In this article we have seen:

  • How to create a library package
  • How to create a library package that depends on another library package
  • How to create an executable that depends on a library package
  • How to import the systemย Glibc module into our executables.

The Swift Package Manager simplifies many aspects of the development process for us. Many of the things we have discussed also work on macOS. Going forward reusing code and planning for the same should be done keeping Swift Package Manager in mind.

Programming Style Guide: Documentation

Now we will shift our attention to that part of programming which is often ignored. Documentation.

Documentation is a key part of programming. In fact, some might go as far as saying that Documentation is the most important aspect of Programming. Let us understand what we mean by documentation by looking at some key points. Later we will look at different ways of documenting our code.

We document our code so that:

  1. Anyone who is reading our code can understand what we are trying to achieve.
  2. Anyone who wishes to make changes to our code knows where to make the changes.
  3. Anyone who issuing our code can easily find out its capabilities and limitations.
  4. Other programmers can figure out how to use our code.
  5. Developers can find out when and where changes were made to a code. This is useful to understand the evolution of our code.
  6. We can easily recollect what, why, when, where & how something was done by us. This is necessary if we are revisiting code that we have written a long time back.
  7. We can add warnings and disclaimers

There may be some other reasons why we may want to document our code, but the list above summaries the most common reasons. This can easily be seen from a simple example.

func fahr_to_cent(Centigrade temp : Float) -&amp;gt; Float
{
return (32 + (temp * 1.8))
}

It is clear to use what the function does simply from its name. However, there is a lot more information that we can provide. Let us modify the implementation a little bit to make it more informative and readable.

/**
This function takes temperature in Centigrade and converts it to Fahrenheit.
- important: This function does not do data validation
- parameter temp: This is the temperature in Centigrade. It can be a negative value too.
- returns: This is the temperature in Fahrenheit.
- requires: `temp > -273.0 && temp < 1000.0`
- Note: The requirement mentioned is not enforced.
- Since: iOS 11
- author: Arun Patwardhan
- copyright: Copyright (c) Amaranthine 2015
- version: 1.0
*/
func convert_to_fahrenheit_from(Centigrade temp : Float) -&amp;gt; Float
{
     return ((temp * 9.0 / 5.0) + 32.0)
}

The code above looks a lot better now. We made the function name better, but more importantly we have added documentation that better describes the function. This includes range of permitted values, version number, important notes. The comments haven’t been written randomly. They have been formatted in a way that will make them appear in quick help. So now if we have to use the function we know what to watch out for.

Now that we know why we need to document our code let us look at some of the ways this can be done.

Comments

The most common form of documentation is by using comments. Most programming languages support comments. Comments are text which is ignored by the compiler. As such they are not used to build the actual software. The sole reason why they exist is because there has to be some mechanism to write notes.

Single Line Comments

// This is a comment

A single line comment as the name says is a piece of text that can fit in one line.

Good when a short description is required. Normally this is placed before or after a variable as most variables would need a short description.

You can have multiple lines using the Single comment mechanism too.

// This is a comment
// This is a comment on the next line

Multi Line Comments

There is a better way to implement multi line comments. We can enclose the text in aย /* */ range.

/* This is a comment
   This is a comment on the next line
   Useful when we have to write really large pieces of comments&amp;amp;amp;lt;span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			&amp;amp;amp;gt;&amp;amp;amp;lt;/span&amp;amp;amp;gt;
*/

Use Case

Here are some examples of when comments can or should be used.

/*
        File Name.   : main.cpp
        Date Created : 13th February 2017
        Created By   : Arun Patwardhan
        Project Name : String Parser
        File Contents:
                - Command Line Option selector
                - Different entry points for the remaining code
        Contact      : arun@amaranthine.co.in
*/

This is a classic example of a multi line comment. This comment provides useful information about the name of the file, when it was created, who created it, contact information, the code that is found in this file.

/*
    Exception Possibilities while Reading/Writing from/to Database
    write_error : This is thrown when there is duplicate data that is being
                  written into the database.
    db_empty.   : This is thrown when you attempt to read from an empty data
                  base.
                  Use the func is_empty() method.
    invalid_data: This is thrown when the data to be written is not valid.
    data_missing: This is thrown when insufficient data is passed. If the write
                  operation requires mandatory data an exception is thrown
                  instead of writing default values.
*/
enum DBExceptions : Error
{
    case write_error(String)
    case db_empty(String)
    case invalid_data(String)
    case data_missing(String)
}

This example shows the necessary documentation while declaring a new type. In short its purpose and situations when different values might be used.

Here is an example of code for functions.

@interface Converter : NSObject
/*!
    @brief This is a temperature conversion function

    @discussion This functions takes floating point values and does a floating point conversion to make sure that we get a precise conversion.

    @param temperature This is the value in centigrade that is passed in. Note, negative values can also be passed in. Values whose results exceed the range supported by float will produce un predictable results.

    @return float Returns a floating point value
*/
-(float) convert_to_fahrenheit_from_centigrade:(float) temperature;
@end

The comment gives information about different aspects of the function. Including the rage of values supported. Note that it also uses special markup to allow for the code description to show up in the Help menu bar or when you option click the method.

Comments

This is how the comments with markup look like. They appear in the โŒฅ click menu as well as the help menu on the right hand side.

Read Me Files

Another thing one can do along with comments is to create Read Me files. Read Me files are plain text files that are bundled as a part of the project. Unlike comments which give information about a specific piece of code or an entire file, Read Me files give information about the entire project as a whole. Since they are text files we actually treat them as text.

Here is some typical information that is found in a Read Me file:


Project Name : String Parser
Project Request/Ticket Code: 13788
Orignal Project Author : Arun Patwardhan
Contact Details :
– arun@amaranthine.co.in
http://www.amaranthine.in

Platforms on which Application Can Run
– macOS 10.10 or later
– Windows 7 or later
– Linux (Ubuntu 14 or later)

Compiler Supported – g++

Building the Application

make

Testing

strParser -f Test1 -o myOutput1
strParser -f Test2 -o myOutput2

Files
– makefile
This is the file used to build the Application.

– main.cpp
This is the entry point file. The selection of execution path on the basis of command line options is done here.

– Parser.h
This file contains the declaration for the Parser class as well as its internal structure.

– Parser.cpp
This file contains the implementation of the Parser class

– DataStructure.h
This file contains the declaration of the internal structure of the data structure.

– DataStructure.cpp
This file contains the implementation of the internal structure of the data structure.

– Validator.h
This file contains the declaration of the internal structure of the data structure.

– Validator.cpp
This file contains the implementation of the internal structure of the data structure.

– Test1
Runs a basic set of strings as input.

– Output1
Expected output after running Test1. Compare your results with the results of this file.

Libraries Required – Standard Template Library


The above is just a sample Read Me file. In real world implementations these can get a lot bigger with references to links and future developments. Some of the other things that can be mentioned are:

  • Future additions
  • Bugs fixed (potentially with the bug fix request ticket)
  • Limitations
  • Tools that are required to make this code
  • Additional tools that need to be installed
  • Project Status

Naming Conventions

Documentation becomes a lot easier if we follow good naming conventions. Variables, functions, types, files… which are well named in itself become self explanatory and at the very least reduce the amount of documentation required.

Additional Tools Documentation in C++, Objective-C

Doxygen

HeaderDocretiredย You may come across some projects that use this.

Additional References for Documentation for Swift

Here is an article on Markups for Swift.

 

Programming Style Guide: Code Refactoring

One of the key attributes towards code that is readable and easy on the eyes is code that is split into appropriately sized pieces. Code refactoring is does exactly that. It is very easy to write a program as one big piece of code. Of course, any program that grows becomes increasingly complicated and highly inefficient. If not controlled, it will soon reach a point where it is highly unreadable, extremely difficult to maintain & filled with bugs. Not to mention that it is inefficient too.

Refactoring code and breaking it down into smaller reusable chunks is the key. The objective is:

  1. To make code easier to read
  2. To make reusable components so that we can save on duplication of code. This will reduce the code count and make sure that any changes to the reused code are available everywhere.
  3. To lend a structure to the application. Tasks now have their own space.
  4. Build scalable and maintainable code.
  5. Build bug free code.

Let us look at an example.

Screen Shot 2017-10-16 at 11.26.26 AM

Bad Code

This code is clearly written poorly. Its difficult to read. There aren’t good whitespaces. No consistency. Even the naming conventions are poor.

The fix would be :

  • Break it down into different functions
  • Separate tasks into their own files
  • Name the different elements of the code properly.

This is how the code looks now. It has been broken down into different files.

main.cpp

#include <iostream>
#include "MathOperations.hpp"
#include "Choices.hpp"

int main(int argc, const char * argv[])
{
     float number1 ย  ย  ย  ย  ย  = 0.0;
     float number2 ย  ย  ย  ย  ย  = 0.0;
     Choices selectedOptionย  = CLEAR;
ย  ย   float answerย  ย  ย  ย  ย  ย  = 0;
ย  ย   float integralAnswerย  ย  = 0;

ย  ย   while(EXIT != selectedOption)
ย  ย   {
          //Welcome message
ย  ย  ย   ย   std::cout<<"Welcome to Calculator Program"<<std::endl;
ย  ย  ย  ย    std::cout<<"Choose between the following options"<<std::endl;
ย  ย  ย  ย    std::cout<<"1. Add\n2. Subtract\n3. Multiply\n4. Divide\n5. Remainder\n6. Percentage"<<std::endl;

ย  ย  ย  ย    //User choice
ย  ย  ย  ย    std::cout<<"Choice: "; ย  ย  ย  ย   ย  ย  ย  ย      ย     ย    std::cin>>selectedOption;

ย  ย  ย  ย    //Chance to enter first number
ย  ย  ย  ย    std::cout<<"Number 1: "; ย  ย  ย  ย               ย     ย    std::cin>>number1;

ย  ย  ย  ย    //Chance to enter second number
ย  ย  ย  ย    std::cout<<"Number 2: "; ย  ย  ย  ย               ย     ย    std::cin>>number2;

ย  ย  ย  ย    switch (selectedOption)
ย  ย  ย  ย    {
               case ADDITION:
                    answer = addition(number1, number2);
ย  ย  ย  ย  ย  ย   ย  ย     std::cout<<"The addition of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
               case SUBTRACTION:
                    answer = subtraction(number1, number2);
ย  ย  ย  ย  ย  ย  ย  ย      std::cout<<"The subtraction of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
               case MULTIPLICATION:
                    answer = multiplication(number1, number2);
ย  ย  ย  ย  ย  ย  ย  ย      std::cout<<"The multiplication of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
               case DIVISION:
                    answer = division(number1, number2);
ย  ย  ย  ย  ย  ย  ย  ย      std::cout<<"The division of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
ย  ย  ย  ย  ย  ย     case REMAINDER:
                    integralAnswer = remainder((int)number1, (int)number2);
ย  ย  ย  ย  ย  ย  ย  ย      std::cout<<"The remainder of "<<number1<<" divided by "<<number2<<" = "<<integralAnswer<<std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
ย  ย  ย  ย  ย  ย     case PERCENTAGE:
                    answer = percentage(number1, number2);
ย  ย  ย  ย  ย  ย  ย  ย      std::cout<<"The percentage of "<<number1<<" out of "<<number2<<" = "<<answer<<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span><std::endl;
ย  ย  ย  ย  ย  ย  ย  ย      break;
ย  ย  ย  ย  ย  ย     default:
ย  ย  ย  ย  ย  ย  ย  ย      break;
          }
     }
     return 0;
}

Choices.hpp

#ifndef Choices_hpp
#define Choices_hpp

#include <stdio.h>
#include <iostream>

enum Choices : unsigned short int { ADDITION = 1, SUBTRACTION, MULTIPLICATION, DIVISION, REMAINDER, PERCENTAGE, CLEAR, EXIT};

typedef enum Choices Choices;

std::istream& operator >>(std::istream &is, Choices& enumVar);

#endif

Choices.cpp

#include "Choices.hpp"

std::istream& operator >>(std::istream &is, Choices& enumVar)
{
    unsigned short int intVal;
    is>>intVal;
    switch (intVal) {
        case 1:
            enumVar = ADDITION;
            break;
        case 2:
            enumVar = SUBTRACTION;
            break;
        case 3:
            enumVar = MULTIPLICATION;
            break;
        case 4:
            enumVar = DIVISION;
            break;
        case 5:
            enumVar = REMAINDER;
            break;
        case 6:
            enumVar = PERCENTAGE;
            break;
        default:
            enumVar = EXIT;
            break;
    }
    return is;
}

MathOperations.hpp

#ifndef MathOperations_hpp
#define MathOperations_hpp

#include <stdio.h>

//Addition
float addition(float number1, float number2);

//Subtraction
float subtraction(float number1, float number2);

//Multiplication
float multiplication(   float number1, float number2);

//Division
float division(float number1, float number2);

//Remainder
int remainder(int number1, int number2);

//Percentage
float percentage(float number1, float number2);

#endif

MathOperations.cpp

#include "MathOperations.hpp"

//Addition
float addition(float number1, float number2)
{
    return number1 + number2;
}

//Subtraction
float subtraction(float number1, float number2)
{
    return number1 - number2;
}

//Multiplication
float multiplication(   float number1, float number2)
{
    return number2 * number1;
}

//Division
float division(float number1, float number2)
{
    if (number2 > 0) {
        return number1 / number2;
    }
    return 0.0;
}

//Remainder
int remainder(int number1, int number2)
{
    return number1 % number2;
}

//Percentage
float percentage(float number1, float number2)
{
    if (number2 > 0) {
        return (number1 / number2) * 100.0;
    }
    return 0.0;
}

Let us look at how this looks for Swift.
main.swift

import Foundation

var number1 : Float ย  ย  ย  ย  ย  ย  = 0.0
var number2 : Float ย  ย  ย  ย  ย  ย  = 0.0
var selectedOption : Choicesย  ย  = Choices.CLEAR
var answer : Floatย  ย  ย  ย  ย  ย  ย  = 0.0
var integralAnswer : Intย  ย  ย  ย  = 0

func readNumbers(One firstNumber : inout Float, Two secondNumber : inout Float)
{
     //Chance to enter first number
     print("Number 1: \n")
     firstNumber = Choices.inputNumbers()

     //Chance to enter second number
     print("Number 2: \n")
     secondNumber = Choices.inputNumbers()
}

while(Choices.EXIT != selectedOption)
{
     //Welcome message
     print("Welcome to Calculator Program")
     print("Choose between the following options")
     print("1. Add\n2. Subtract\n3. Multiply\n4. Divide\n5. Remainder\n6. Percentage")

     //User choice
     print("Choice: \n")
     selectedOption = Choices.inputChoices()
     switch (selectedOption)
     {
          case Choices.ADDITION:
               readNumbers(One: &number1, Two: &number2)
               answer = addition_of(_value: number1, with_value: number2)
               print("The addition of \(number1) & \(number2) = \(answer)")
               break
          case Choices.SUBTRACTION:
               readNumbers(One: &number1, Two: &number2)
               answer = subtraction_of(_value: number1, from_value: number2)
               print("The subtraction of \(number1) & \(number2) = \(answer)")
               break
          case Choices.MULTIPLICATION:
               readNumbers(One: &number1, Two: &number2)
               answer = multiplication_of(_value: number1, with_value: number2)
               print("The multiplication of \(number1) & \(number2) = \(answer)")
               break
          case Choices.DIVISION:
               readNumbers(One: &number1, Two: &number2)
               answer = division_of(_value: number1, by_value: number2)
               print("The division of \(number1) & \(number2) = \(answer)")
               break
          case Choices.REMAINDER:
               readNumbers(One: &number1, Two: &number2)
               integralAnswer = remainder_of(_value: Int(exactly:number1)!, <span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>divided_by_value: Int(exactly: number2)!)
               print("The remainder of \(number1) divided by \(number2) = \(integralAnswer)")
               break
          case Choices.PERCENTAGE:
               readNumbers(One: &number1, Two: &number2)
               answer = percentage_of(_value: number1, with_respect_to_value: number2)
               print("The percentage of \(number1) out of \(number2) = \(answer)")
               break
          default:
               selectedOption = .EXIT
               break
     }
}

Choices.swift

import Foundation

enum Choices { case ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION, REMAINDER, PERCENTAGE, CLEAR, EXIT}

//CLI Reading Capability
extension Choices
{
    static func inputChoices() -> Choices
    {
        let ip : String? = readLine()
        let choice : String = String(ip!)

        switch choice {
        case "1":
            return .ADDITION
        case "2":
            return .SUBTRACTION
        case "3":
            return .MULTIPLICATION
        case "4":
            return .DIVISION
        case "5":
            return .REMAINDER
        case "6":
            return .PERCENTAGE
        default:
            return .EXIT
        }
    }

    static func inputNumbers() -> Float
    {
        let ip : String? = readLine()

        let numberFormatter = NumberFormatter()
        let number = numberFormatter.number(from: ip!)

        let num : Float? = number?.floatValue
        return num!
    }
}

MathOperations.swift

import Foundation

//Addition
func addition_of(_value number1 : Float, with_value number2 : Float) -> Float
{
    return number1 + number2;
}

//Subtraction
func subtraction_of(_value number2 : Float, from_value number1 : Float) -> Float
{
    return number1 - number2;
}

//Multiplication
func multiplication_of(_value number1 : Float, with_value number2 : Float) -> Float
{
    return number2 * number1;
}

//Division
func division_of(_value number1 : Float, by_value number2 : Float) -> Float
{
    if (number2 > 0) {
        return number1 / number2;
    }
    return 0.0;
}

//Remainder
func remainder_of(_value number1 : Int, divided_by_value number2 : Int) -> Int
{
    return number1 % number2;
}

//Percentage
func percentage_of(_value number1 : Float, with_respect_to_value number2 : Float) -> Float
{
    if (number2 > 0) {
        return (number1 / number2) * 100.0;
    }
    return 0.0;
}

Discussion on Swift Extensions

As we can see that most of the code in Swift is very similar to C++. Most of the differences are basic syntactic differences. However, there is 1 feature of Swift that greatly aids code refactoring that I would like to talk about, Extensions.

Extensions allow us to add new functionality to the existing type. As the name says the type is extended. This allows us to add changes to a type in a consistent & clearly demarcated way. Developers can now neatly separate newly added components. This greatly helps in understanding the evolution of types.

“This is often referred to as versioning.”

Extensions can be used in the following ways to implement code refactoring:

  • Different sections of a type reside in their own extensions
  • Changes made to a type are made by keeping them in their own extensions
  • Step by step build up of code is done by representing each step as an independent extension. This gives clarity on how a certain end result was achieved.

Conclusion

As we can see from the sample code aboveย (for both C++ & Swift) the program is much more readable. Code is carefully compartmentalised. Its a lot easier to read. It is a lot easier to scale too.

The reader may point out that the amount of code to achieve the same result is significantly higher, that however is a small price to pay in the long run. The biggest advantage is the scalability & the ease with which it can be done. Simply breaking code down into separate files & functions makes a huge difference. Here are some other benefits:

  • Individual files can be modified. This means one can now have a team working on different parts of the code.
  • Code is less cluttered. Changes are now spread across files & are easier to track.

We will now see how we can further improve this code in upcoming articles.

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.