I’m pretty sure you already know what cocoapods is, but just in case, it’s a dependency manager for iOS and OS X. It’s a great little thing that makes importing libraries into your projects a breeze. I’ll assume you know how to use cocoapods, so this post will focus on creating your own cocoapod and sharing it with the open source community.
Intro
If you remember the times when we were importing libraries by hand, you’ll appreciate dependency managers like cocoapods or carthage. If we wanted to import a library, just adding a library to your project wasn’t enough, you had to link your project to additional frameworks, import additional libraries, there was some work involved. Now, you simply create a list of the libraries you want to use in a pod file, and cocoapods sorts all these things out for you.
How Does It Work?
Cocoapods is a distributed dependency manager. What this means is that there’s a repository of all the pods called ‘Specs’ repo. Every single person/mac using cocoapods has a local copy of this ‘Specs’ repo, you get your copy when you run pod update from the terminal. That’s what makes it distributed. Every cocoapod is just a file with an extension ‘.podspec’, it’s pretty simple and contains all the info needed to fetch a library into a project. The thing is, the file is pretty small, but there’s a lot of them in the ‘Specs’ repo, so when you do your pod update you’ll fetch every single podspec there is, and there are thousands of them, so your initial pod update can take a couple of minutes to finish.
You list the libraries that you want to use in a file called ‘Podfile’, you create this file by running pod init in the terminal. Run pod install, and cocoapods will run through all the libraries you want, import them, and import all the dependencies that those libraries need, it will set your project to use them, link against the frameworks, you don’t have to do anything, it’s really cool 🙂
Creating a Project
Cocoapods does a lot of things for you, including creating the whole project for development, and a template for the example app, all you have to do is run:
pod lib create DAKeychain
And right here you can encounter a problem. You might miss some of the dependencies that cocoapods needs to finish creating your project. I was missing a library ‘colored2’, you’ll see a message like this in the console:
dejan$ pod lib create DAKeychain Cloning `https://github.com/CocoaPods/pod-template.git` into `DAKeychain`. Configuring DAKeychain template. /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:126:in `require': cannot load such file -- colored2 (LoadError) from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:126:in `require' from /Users/dejan/DAKeychain/DAKeychain/setup/TemplateConfigurator.rb:2:in `<top (required)>' from ./configure:5:in `require_relative' from ./configure:5:in `block in <main>' from ./configure:4:in `each' from ./configure:4:in `<main>' To learn more about the template see `https://github.com/CocoaPods/pod-template.git`. To learn more about creating a new pod, see `http://guides.cocoapods.org/making/making-a-cocoapod`.
So let’s install the missing library:
sudo gem install colored2
When you’re finished installing colored2 run the ‘lib create’ again, if everything works, cocoapods will ask you a few pretty straightforward questions:
What language do you want to use?? [ Swift / ObjC ] > Swift Would you like to include a demo application with your library? [ Yes / No ] > Yes Which testing frameworks will you use? [ Quick / None ] > None Would you like to do view based testing? [ Yes / No ] > No
When you’re finished answering the questions your XCode should open with the project. There you have it 🙂 In your project navigator, at the top, you’ll see ‘Podspec Metadata’ you’ll need to edit your .podspec file, and it would be nice if you updated the readme.md file as well 🙂 .podspec file is really important, so let’s just go over the file for ‘DAKeychain’:
Pod::Spec.new do |s| s.name = 'DAKeychain' s.version = '0.1.0' s.summary = 'DAKeychain is a simple keychain wrapper that acts like a dictionary.' s.description = <<-DESC I created this small library for fun. I wanted to abstract all that boilerplate code when working with the keychain and I wanted to make working with the keychain as simple as possible. So I created this. This library will behave like a dictionary when working with the keychain. It's pretty simple to use. DESC s.homepage = 'https://github.com/dagostini/DAKeychain' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'dagostini' => 'dejan.agostini@gmail.com' } s.source = { :git => 'https://github.com/dagostini/DAKeychain.git', :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/dagostin' s.ios.deployment_target = '8.0' s.source_files = 'DAKeychain/Classes/**/*' end
‘name’, ‘version’ and ‘source’ are really important, so pay close attention when adding those. Other than that the file is pretty simple. If your library has dependencies to other libraries, you would add those in here (there’s a property ‘dependencies’ that you could use). Our library is pretty simple, and it has no dependencies. A thing to note here is the version number. Cocoapods is using semantic versioning, no one can force you to use it in your project, but it’s considered common courtesy to use it.
If you go to your ‘Pods’ project and expand the ‘Development Pods’ folder, you’ll end up in the ‘Classes’ folder, you’ll add your classes here. I just copied my ‘DAKeychain’ here and deleted the auto-generated file. I also had existing tests that I copied in the ‘Tests’ folder in the ‘DAKeychain’ project. Just make sure you add the file to your test target when you add it in. I added a simple method in the ‘AppDelegate’ just as an example:
private func exampleUsage() { DAKeychain.shared["key"] = "confidential data" // Store let value = DAKeychain.shared["key"] // Fetch print("Fetched value: ", value ?? "FETCH FAILED") }
You’ll need to import the pod as well, so add this to the top of the file:
import DAKeychain
Now let’s just build and run so we can watch it fail 🙂
Fixing Up The Project
When you build and run your build will fail. Your classes need to be public for you to use them in pods. Since we’re using swift 3 we’ll mark our class as open. Now when we build and run, everything should work.
If we run the unit tests everything will build and run just fine, but the tests won’t execute. The problem is, the host app was not set on the test target, it’s an easy fix. Select your test target, and under ‘General’ tab you’ll see the ‘Host Application’, select your app target, and you should be all set. Take a look at the image below:
Now your project should be all set and ready to be submitted.
Preparing For Submission
We’re using swift 3, so we’ll need the prerelease version of cocoapods, so let’s just install it quickly:
sudo gem install cocoapods --pre
Before submitting your pod in the Specs repo, you need an account on the cocoapods trunk. It’s pretty easy to setup, simply run a command like:
pod trunk register dejan.agostini@gmail.com 'Dejan Agostini' --description='macbook pro'
After running this step you’ll get an email, go ahead and verify your account, now you’re almost ready to submit your pod 🙂
If you’re not using swift 3 you can submit the pod right now, but we’re using swift 3, so we need an extra step. If we were to push our podspec to the Specs repo we would see an error like this:
[!] The spec did not pass validation, due to 1 error. [!] The validator for Swift projects uses Swift 3.0 by default, if you are using a different version of swift you can use a `.swift-version` file to set the version for your Pod. For example to use Swift 2.3, run: `echo "2.3" > .swift-version`.
The error gives us the solution as well, so let’s just run it in the terminal:
echo "3.0" > .swift-version
If you read the error, and then read what I wrote in the terminal, you might be puzzled like me. But it turns out my podspec wouldn’t validate until I created this file, even though I was using swift 3.
Now commit and push your project to GitHub (or whatever you’re using), and make sure you create a branch that’s the exact name of your version (in my case it was ‘0.1.0’).
OK, now we’re ready to push the podspec, so let’s push it:
pod trunk push DAKeychain.podspec
If all went well, you should see this in the console:
🎉 Congrats 🚀 DAKeychain (0.1.0) successfully published 📅 April 26th, 00:11 🌎 https://cocoapods.org/pods/DAKeychain 👍 Tell your friends!
If you wait 15-ish minutes, you’ll see your pod on cocoapods.org, so go ahead and search for it:
Congratulations, you created your first pod, finally 🙂
Testing The Pod
Let’s create a simple project, just to quickly test if our pod actually works. Name your project whatever you want (I called mine DAKeychainTest), and in the root directory of your project run pod init to create your podfile, in your podfile add your new pod:
# Uncomment the next line to define a global platform for your project platform :ios, '10.0' target 'DAKeychainTest' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for DAKeychainTest pod 'DAKeychain' end
Now if we run pod install, we’ll get an error:
DejansMacBookPro:DAKeychainTest dejan$ pod install Analyzing dependencies [!] Unable to find a specification for `DAKeychain`
Seems weird, we did everything correctly, yet, our pod is not there. Turns out we need to update our local pod specs, so let’s just run pod update:
DejansMacBookPro:DAKeychainTest dejan$ pod update Update all pods Updating local specs repositories $ /usr/bin/git -C /Users/dejan/.cocoapods/repos/master fetch origin --progress From https://github.com/CocoaPods/Specs 4d2cfbbb41d..31767ba33e5 master -> origin/master $ /usr/bin/git -C /Users/dejan/.cocoapods/repos/master rev-parse --abbrev-ref HEAD master $ /usr/bin/git -C /Users/dejan/.cocoapods/repos/master reset --hard origin/master HEAD is now at 31767ba33e5 [Add] DAKeychain 0.1.0 Analyzing dependencies Downloading dependencies Installing DAKeychain (0.1.0) Generating Pods project Integrating client project [!] Please close any current Xcode sessions and use `DAKeychainTest.xcworkspace` for this project from now on. Sending stats Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
And there we are, we have our pod integrated into our project. Now we can use it like we would use any other pod out there.
Conclusion
It’s been a bit of a journey, creating your pod, with a few gotchas, but when you create your first one, the second one is a lot easier. We’re all used to using third-party libraries developed by people in the open source community, this is a great opportunity for you to give something back and contribute a bit. If you have a library that you created, just go and publish it, I’m sure there’s a developer out there who’s looking for exactly what you have. As they say, sharing is caring 🙂
I hope you’ll start creating your pods and share some of your awesome code 🙂
Have a nice day 🙂
Dejan.
Nice article but reminds me that I have to update two private and two public pods next week 🙂
Thanks 🙂
why does this process have to be so complicated? the complicated structure of what pod creates as a template, dealing with unit tests, even registering the pod to a pubic repo (and this tutorial is linked to by the private pod tutorial which is confusing). surely, there’s an explanation that shows the minimal setup required. e.g. creating 1 or 2 files with the right configs and maybe a single swift file that exports a function.
Hi,
Sorry, I can’t really help you there 🙁 This is pretty much a standard template for creating pods. Once you create a few pods it becomes a lot easier.
Hi I created pod same as above but when i opened cocoapods.org, and search for my pod it’s not showing. I do not know how to resolve this one. Can u please help me.
Thanks in Advance.