How to Release an Open Source iOS Library

In this post, we’ll go over how to release an open source iOS library and distribute it using the dependency manager CocoaPods.

As an example, we’ll use a super simple barcode scanning library I created that takes advantage of the new iOS 7 functionality called MTBBarcodeScanner.

For one of my side projects, an app that manages your book collection, I wanted to show the camera feed inside a UIView and have a simple block-based callback when barcodes are captured.

I couldn’t find any third-party libraries out there that already did this, so I went ahead and created my own. A few days later, I decided to make it open source!


Prerequisites

In order to keep this post a reasonable size, I’m going to assume you’re comfortable with git and GitHub.

If you’d like to read more about git, there’s a free reference book available online. As far as GitHub, their support site is a useful resource for beginners.


Why create an open source library?

“The best way to find yourself is to lose yourself in the service of others.” — Mahatma Gandhi

  • Sharing is rewarding. Open source projects have saved me countless hours of development time. Knowing other people will benefit from your code is a nice feeling!

  • Learn from others. Writing software is full of challenges. Releasing your code to the world allows people to suggest changes, add improvements, and share their knowledge.

  • It’s never been easier. GitHub and BitBucket have revolutionized how people share code. Nowadays, your software is only a git push away from being globally accessible.

  • Build great things collaboratively. Amazing things can be created when the right team of collaborators comes together.

  • Build your reputation and resume. Open source contributions look good on resumes. Not only are you giving back to the development community, you’re also providing high-quality programming examples of your work to potential future employers.

When designing an open source project, the first and most important step is to define your project.


Define Your Project

It’s important to have a clear vision of your project’s goals so it can grow in a natural way. Without a clear vision, feature creep can sneak up on you and turn your software into bloatware.

The earlier you define these goals, the better. Every project should answer these questions:


What value does this bring to people?

If your project is cool and gimmicky but provides no real value, it might not be worth the effort to release as open source. It’s not enough to throw some code into and repo - you have to provide value to someone!

As far as MTBBarcodeScanner goes, here’s the value I believe it adds to the universe:

The first value is ease of use.

Learning how to scan barcodes in iOS 7 requires a bit of a learning curve. I found it took several hours before I was proficient enough to scan my first code. With MTBBarcodeScanner, you can scan your first barcode in no time with one very simple method:

[self.scanner startScanningWithResultBlock:^(NSArray *codes) {
  AVMetadataMachineReadableCodeObject *code = [codes firstObject];
  NSLog(@"Found code: %@", code.stringValue);
}];

The project’s second most important value is flexibility.

When searching for a good open source barcode scanning library for iOS I found they all made too many assumptions about my use case. Some projects put the barcode scanning on a separate view controller, others forced you to stop scanning after capturing one barcode. Neither case would work for my app.

With MTBBarcodeScanner, you can capture as many barcodes as you want and present the camera input in a UIView. The callback block provides more information than most other libraries, making the library incredibly flexible.

Here’s an example of how you can draw Valid or Invalid, depending on the code’s content, on the barcodes as they appear in the live preview:

<img src="/images/posts/open-source-ios-library/advanced.png" width=30% height=30% />

This example can be found in the demo project under Advanced Example.


What are the design goals for this project?

Creating a set of guidelines for your project is important. Contributors can use these guidelines to better understand the project and it’s future direction. RNCryptor is a great example of this, and has a very clear set of goals outlined in it’s README file.

For my project, the goals are to:

  • Provide an easy, flexible way to scan barcodes
  • Ensure scanning is fast and reliable
  • Leave a small footprint
  • Keep the control in the hands of the user. Don’t make too many assumptions about how they want to use the library. For example:
    • Don’t assume the user wants to scan one code at a time
    • Don’t assume the camera input view should be a particular size
    • Don’t assume the scanning process will have it’s own view controller

Where do you see this library going in the future?

In the future, I see MTBBarcodeScanner evolving in a few different ways:

  • Performance enhancements. With some fine-tuning, the scanning process can be sped up.
  • Added flexibility. It would be nice to expose more options to configure the finer details of scanning, including the resolution preset, focal point, etc. The library would use sane defaults but allow configuration for anyone who needs more advanced control.

I can imagine the following features being added in the near future:

  • LED support. It would be nice to add support for turning the LED on and off for scanning during low-light conditions.
  • Highlight barcodes in live preview. I have an example of this in the demo project, but it might be helpful to add this functionality to the library itself.

I see the project being maintained for backwards-compatibility when iOS 8+ comes out, but I don’t see ever adding support for iOS 6. Adding support for iOS 6 would require using a library like ZXingObjC, which is fairly large and goes against the project’s 4th design rule: leave a small footprint.


Which license will you choose?

Most of the open source projects I encounter during iOS development are released under an MIT license, but there are plenty of other open source licenses available.

The Open Source Initiative website has a good list of available licenses. If you’re having trouble deciding which one to use, check out the Choose A License site!


Creating Your Project

Project Structure

Keep your project organized in a sane way. If your project gains some popularity, people will be more likely to contribute if it’s kept clean and organized. Nobody wants to work with a library that has dozens of files all in the root file directory.

If you’re creating a project that will be distributed with Cocoapods, the easiest way to create the directory structure is to use the CocoaPods gem. If you don’t have it installed, go ahead and install it with:

gem install cocoapods.

Then, to create your directory structure based on the CocoaPods template, run:

pod lib create ABCYourLibraryName

It’s common practice to prefix your libraries with 2 or 3 characters to avoid conflicts with other libraries and Apple’s frameworks. Apple recommends three characters, but there are plenty of really popular libraries that use only two.


Creating the Code

When writing your project, try to make the code as clean, modular, and easy to understand as possible. The cleaner the code, the more likely you are to attract contributors. Don’t just throw something together and expect anyone to sort through the mess!


Documentation

Be sure to document your code thoroughly. Without good documentation, an open source project won’t attract nearly as many contributors (or satisfied users, for that matter!).

For iOS projects distributed with CocoaPods, HTML documentation is automatically generated from your comments and displayed using CocoaDocs. As an example, here’s the documentation generated for the MTBBarcodeScanner project.

If you’re unsure of the best way to document your software, there’s a great article on the subject from NSHipster.


Versioning

If you’re distributing through CocoaPods, you should version your project using Semantic Versioning.

With this system, version numbers appear in the format MAJOR.MINOR.PATCH. Keeping track of dependencies is a lot easier when everyone follows the same format for versioning!


Unit Testing Considerations

It’s not a requirement, but you should consider adding unit tests to your project. A good set of unit tests can be extremely valuable for testing the functionality of your project.

If a contributor fixes an issue in one area of your project but breaks something in another area, unit tests can help you find the issue, and quick!


Distribution

Why distribute through CocoaPods?

With over 3500 libraries, CocoaPods is the largest dependency manager for Xcode. Adding your library to the official repository is as simple as submitting a pull request on GitHub.

The easier I can make it for people to use my library the better. With CocoaPods, it’s as simple as adding pod MTBBarcodeScanner to a Podfile .


Creating a Podspec

A .podspec file tells CocoaPods how to integrate your project into a system. You submit your project’s podspec file to the official CocoaPods/Specs repo, which lets people download your project using their Podfile.

The official CocoaPods guide has a very thorough tutorial on the process of creating a podspec file.

Here’s the podspec file for MTBBarcodeScanner, with a few links shortened for the sake of formatting:

Pod::Spec.new do |s|
  s.name             = "MTBBarcodeScanner"
  s.version          = "0.1.1"
  s.summary          = "A barcode scanning library for iOS 7."
  s.homepage         = "GitHub URL"
  s.license          = 'MIT'
  s.author           = { "Mike Buss" => "mike@mikebuss.com" }
  s.source           = { :git => "	URL", :tag => "0.1.1" }

  s.platform              = :ios, '7.0'
  s.ios.deployment_target = '7.0'
  s.requires_arc          = true

  s.source_files = 'Classes/ios/**/*.{h,m}'
  s.frameworks   = 'AVFoundation', 'QuartzCore'  
end

After you’ve created your podspec file, be sure to run a pod spec lint command to check if it passes validation. The output this command generates is extremely helpful for fixing any issues that might appear.

Your pod will be rejected if it contains any warnings or errors, so be sure to fix them before submitting!


Creating a README

The README file is arguably the most important file in your project. This is the first file your users will see, so it needs to make a good first impression!

Every README should include the following elements:

  • Brief introduction to the library
  • Installation instructions
  • Screenshots (if applicable)
  • Usage examples
  • Project goals and values
  • License information
  • Developer contact information

Creating a CHANGELOG

Your CHANGELOG file contains a log of changes made to your project.

Here are the basic elements of a CHANGELOG:

  • The version of the app containing the change
  • The date of the change
  • A brief description of the change (possibly a link to it’s corresponding GitHub issue)
  • Whether the change was a bug fix, added feature, or improvement
  • The names of the contributors that made the change

The AFNetworking library has a great example of a CHANGELOG.


Publishing with GitHub

When you’ve decided your library is ready to share with the world, the next step is to create a repository on GitHub and push your code.

GitHub provides a great set of tools for managing your repository. The two most important features are Issues and Pull Requests.


Issues

The GitHub Issues system is a great way to track changes that need to be made to your project. Whether that change is a bug fix, enhancement, or a new feature, the issue system is a fantastic place to discuss the change with contributors.

Labels are an indispensable way to manage your project’s issues. Here are some labels I would suggest:

  • Bug: The project isn’t functioning as it says it should.
  • Feature: The project could benefit from having an extra feature added. Make sure this fits within your project’s guidelines!
  • Discussion: There exists some relevant discussion that needs to happen between the contributors and users.
  • In Progress: The issue is currently being worked on by a contributor. This might only be necessary after a certain number of contributors join the project.
  • Investigate: There’s an uncertainty about the project, for example if you’re unsure how your library would perform under certain conditions (for example, on an iPad model you don’t own). There might be a contributor who can test this for you!

Accepting Pull Requests

When someone finds an issue in your project, they can fork the repository and contribute changes through pull requests. This is where your project guidelines come in to play - the more thorough your guidelines, the easier it will be for contributors to add appropriate changes to your project.

If your vision for the project is to stay simple and avoid feature creep, you should (politely, and with explanation) reject pull requests that add unnecessary features. If you don’t have a clear set of guidelines, it’s hard for contributors to know how to contribute.


Submitting to the CocoaPods/Specs Repo

After you’ve created your code, documentation, and podspec, the next step is to submit your library’s podspec to the official CocoaPods/Specs repository.

The official CocoaPods guide has great documentation for this process, but here’s a quick overview of the steps involved:

  1. Fork the CocoaPods/Specs repo
  2. Clone your fork locally
  3. Create your project directory, version directory, and add your podspec.
  4. Commit your change
  5. Push to your fork
  6. Submit a pull request to the official CocoaPods/Specs repo
  7. Check the build status of your pull request!

If you’d prefer, you can replace steps 2-5 with the pod push command. When you’re inside the directory with your project’s podspec, run these commands:

$ pod repo add [REPO_NAME] [SPECS_FORK_URL]
$ pod push [REPO_NAME]

Where [REPO_NAME] is an arbitrary name for your Specs fork, and [SPECS_FORK_URL] is the HTTPS URL for your Specs fork.

Here’s an example:

$ pod repo add mb https://github.com/mikebuss/MTBBarcodeScanner.git
$ pod push mb

Then continue with steps 6 and 7.

Important: Be sure to check the build status of your pull request a few minutes after you’ve submitted it. If your branch fails validation, it won’t be merged into the main repo.

If your build passed, you’ll see a green “All is well!” at the bottom of your pull request description:

Passing Build


Congratulations!

If you’ve followed these steps, you’ll end up with an iOS library that can be easily downloaded and integrated into any project using CocoaPods. If you ran in to any issues along the way, please feel free to send me an email.


Lessons Learned

Here are a few things I stumbled on when releasing MTBBarcodeScanner:

When I submitted a pull request to the CocoaPods/Specs repo, the automated build failed. Whoops! Turns out I forgot to increment the version number in my podspec, which meant the podspec in the 0.1.1 folder was pointing to the 0.1.0 tag. A quick “git push –force” fixed the issue, and the build passed. Always double-check your podspec, and make sure the automated build passes after you submit a pull request!

I had forgotten CocoaPods automatically generates documentation for your project using CocoaDocs, so my documentation wasn’t in the correct format. There were also a few methods missing documentation altogether. I went back and updated the documentation. Make sure you document your code thoroughly!

I’ve since started using the VVDocumenter-Xcode to make this process a little easier.


Conclusion

Releasing an open source project is a fantastic way to give back to the development community and hone your skills.

Have a project you released as open source? I’d love to hear!


References