One weird Podfile trick to manage your App farm

We have a lot of apps in Wikia and a lot more to come!

To manage the codebase we're using CocoaPods with our whole code split into local Development Pods. Every app is generated from a thin skeleton project that pulls the rest of the code as Pods.

For every App to have something special, we can add additional Pods only for this one app, and the main Podfile from our skeleton App only includes the App-specific Podfile:

local_podfile = "keep/Podfile"
eval(File.open(local_podfile).read) if File.exist? local_podfile 

Additionally if we want to add some classes or resources to the App, that are not a separate Pod - for instance code that makes use of the above included new Pods - we use this:

post_install do |installer_representation|
    project = Xcodeproj::Project.open('Project.xcodeproj')
    target = project.targets[0]

    code_directory = Dir.pwd + '/keep/Classes/'
    code_directory_glob = code_directory + '**/*'
    res_directory = Dir.pwd + '/keep/Resources/'
    res_directory_glob = res_directory + '*' #include folders as folders, not recursively

    puts "Adding custom code from #{code_directory} directory"
    Dir.glob(code_directory_glob) do |f|

    if project.reference_for_path(f)
        next
    end

    if f.include? '.m'
        libFile = project.new_file("#{f}")
        puts "Adding %s to %s Source Build Phases" % [libFile, target.name]
        target.source_build_phase.add_file_reference(libFile, avoid_duplicates = true)
    elsif f.include? '.h'
        libFile = project.new_file("#{f}")
        puts "Adding %s to %s Copy Headers Phases" % [libFile, target.name]
        target.headers_build_phase.add_file_reference(libFile, avoid_duplicates = true)
    end
    end if File.directory?(code_directory)

    puts "Adding custom resources from #{res_directory} directory"
    Dir.glob(res_directory_glob) do |f|

    if File.directory?(f)
        if Dir[ f +'/*'].empty?
            puts "Directory empty, skipping #{f}"
            next
        end

        first_file = Dir[ f + '/*'][0]
        if project.reference_for_path(first_file)
        	puts "Files from directory already added, skipping #{f}"
            next
        end
    end

    if project.reference_for_path(f)
        next
    end

    libFile = project.new_file("#{f}")
    puts "Adding %s to %s Copy Bundle Resources Phases" % [libFile, target.name]
    target.resources_build_phase.add_file_reference(libFile, avoid_duplicates = true)
    end if File.directory?(res_directory)

    project.save
end

This piece of code, after every pod update adds all the classes and headers from the keep/Classes directory and all the resources from the keep/Resources directory to the project Project.xcodeproj. It is using the Xcodeproj class that is used in Cocoapods internals.

This code has a little caveat. When you add a resource to a subdirectory in keep/Resources (like a new version of a Code Data model) this code will skip it. If anyone knows how to better handle directory references in Xcode projects, please let me know.

One can ask why are we doing this automatically instead of just adding the files manually to the specific App project. Our skeleton project is updated frequently and to update the skeleton in our Apps, we just delete them and regenerate them from the skeleton and do pod update. Nobody has to remember to add some App specific classes to it - everything is handled automatically.