Return to 10to1.be

Under the Hat


All posts by Bob

Cocoapods

30 Jan 2012

Cocoapods

In all of our apps, whether they are web-apps or iOS apps, we make extensive use of different frameworks. These help us to save time by abstracting out stuff that's needed in a lot of different projects.

We also maintain some of our own frameworks: * Tin Makes consuming webservices in iOS a lot easier. * Coby Adds some functionality we know and love from Ruby into several default Obj-C classes.

But these frameworks grow together with our projects, so copying these into several projects can become tedious and managing different versions would make things too complicated.

Ruby has Bundler to solve this problem. Just create a gemfile that lists every "gem" you use, Bundler will install these gems and keep track of the versions in the "Gemfile.lock".

Now there is a similar system for Cocoa, called Cocoapods.

Installing cocoapods

Cocoapods runs on macruby, so installing it using RVM is a breeze:

$ rvm install macruby

Then simply setup cocoapods like this:

$ rvm use macruby
$ macgem install cocoapods
$ pod setup

And now we're ready to start using Cocoapods.

Adding Cocoapods to a project

Using Cocoapods in a new project is quite similar to using Bundler in a rails project. We start by adding a Podfile which could look like this:

platform :ios

dependency "AFnetworking",
  :git => "git://github.com/AFNetworking/AFNetworking.git"

dependency 'Tin',
  :git => "git://github.com/Reprazent/Tin.git",
  :commit => "baf1c407f74dd6ca6c6c8c46bc0a3a565e79d3b6"

dependency 'coby',
  :git => "git://github.com/pjaspers/coby.git"

dependency "MagicalRecord"

The platform can either be :ios or :osx.

Note that, just like with Bundler, you can specify a git repository, and even a commit or tag to reference your dependency. This makes it a lot easier to fix versions and avoid breaking the project when updating the dependencies.

The dependencies referencing a git repository must contain a ".podspec" file. When no repository is specified, Cocoapods looks for the podspec in it's own spec-repository. There's already a bunch of frameworks that are available, and more are sure to follow.

Now we can enable Cocoapods for our project by running pod install for the first time.

$ pod install demoproject.xcodeproj

This will clone the git repositories of the pods specified in a new "Pods" directory in your project root. Just like Bundler it will create a "Podfile.lock" which holds the references to the current version of the pods and its dependecies. Together with a new workspace we can use to open our project in XCode. There we will see that there are actually two projects added to the workspace.

screenshot workspace

One containing your regular project, and one containing the pods, this makes it easier to view the source code of these pods while working.

When building your project your pods will be built into "libPods.a" before building the project itself.

screenshot libPods

Now you're good to go. When adding new pods or updating existing ones in your podfile just run pod install again to update your "Podfile.lock" and adding or updating the Pods.

When multiple build configurations are needed, we also need to add these in the "Pods.xcodeproj".

screenshot build configuration

You should also set "Skip Install" to yes in your build target in order to create archives for different build types.

screenshot skip install

thanks Verbeeckx for figuring these last two out

Creating your own Cocoapods

To be able to add our own frameworks into our own projects easily, we had to add a ".podspec" to the repos. These podspecs look very similar to the gemspecs in Ruby.

Pod::Spec.new do |s|
  s.name     = 'Tin'
  s.version  = '0.0.1'
  s.license  = 'MIT'
  s.summary  = 'Tin makes the internet easier in Cocoa.'
  s.homepage = 'https://github.com/pjaspers/Tin'
  s.author   = { 'pjaspers' => 'piet@jaspe.rs' }

  s.description = 'An optional longer description of Tin.'

  s.source   = { :git => 'https://github.com/pjaspers/Tin.git' }

  s.source_files = 'Classes',
                   'Classes/**/*.{h,m}',
                   "Tin",
                   "*.{h,m}"

  s.dependency 'JSONKit'
  s.dependency "AFNetworking", "~> 0.8.0"
end

In this podspec we set the source, where Cocoapods can find the repository to clone. We specify where the required classes can be found within the repository. We can also set several dependencies, specifying the tags that should be used.

Note that adding a dependency like this, it needs to be present in the spec repository of Cocoapods. When the correct tag or framework isn't present in the repo yet, we can add it ourselves in the Podfile of the project we are using the framework in. In the case of Tin, we had to add "AFNetworking" separately in the Podfile because the highest available version from Cocoapods was 0.7.0.

I suspect Cocoapods will gain a lot in popularity in the future, because it makes adding dependencies to projects much easier. As it gains in popularity, more frameworks will become available.

I suggest you get over to the Cocoapods github and give it a try.

ActiveRecord, Postgres and schemas, lots of them

07 Mar 2011

The problem

One of the projects I'm working on requires me to read a lot of data from multiple d2r servers using SPARQL. Because the data on these servers can sometimes be similar the data has to be filtered before it is made available to a Rails app that will be displaying the data.

I didn't want to litter the database of the Rails app with this duplicate data (because it will feed a d2r server in the future), and fetching 10 rows of the server already took some seconds. So fetching all of the data from multiple servers would take quite a long time.

So I needed a place to store this temporary data separately where it wouldn't bother the data of the Rails app, and be available for filtering when all of the data was fetched. A separate schema for each of the servers would do the job.

Here's how it goes

I didn't load the entire Rails environment for this; this way AR doesn't automatically load the database connection parameters and we can easily adjust them ourself:

dbconfig = YAML::load(File.open(File.dirname(__FILE__) 
        + "/../../config/database.yml"))
@database_connection = dbconfig[Rails.env]
AR::Base.establish_connection(@database_connection)

Creating the schema wasn't that difficult, executing some SQL on the connection did the trick...

AR::Base.connection.execute("DROP SCHEMA 
   IF EXISTS #{@server_name} CASCADE")
AR::Base.connection.execute("CREATE SCHEMA #{@server_name}")

After that we have to alter the connection so AR does all future operations on the newly created schema.

@database_connection[:schema_search_path] = @server_name
AR::Base.clear_all_connections!
AR::Base.establish_connection(@database_connection)

If you do intend to use the entire Rails environment, be sure to do this before the classes are loaded, so they inherit the correct connection. If not you could unload the classes using ActiveSupport::Dependencies.clear.

But in this case I was loading the classes manually, and fetching the data from different servers in different processes. That way the loaded model classes couldn't interfere with each other.

With all the fetching processes finished, the data is ready to be filtered and loaded into the database of the Rails app, and be the source database for another D2R server.


About this blog

You’re reading Under the Hat, a weblog written by 10to1.

Who’s got the mic?