Search code examples
iosswiftcocoapodsswift-package-manager

Share common code between 2 iOS apps: Swift Package Manager? Cocoapods? Monorepo?


I am about to start working on two new iOS apps, that will share a bunch of business logic, like the whole API layer plus the models. Instead of copying the shared code in both projects and trying to keep that in sync, I want to use some way to share the code.

My first though was that I'd use the Swift Package Manager. It's really easy to create a new package in Xcode, but I don't really know how to use the local version of the shared package in both apps while I am working on it, while also making it work for other devs (or CI!) checking out the apps. Can't really commit the apps having a local path dependency, because that would only work for me, right?

So, can I use a local version of that core package while working on the 2 apps and that core package, but still have it all pushed to GitHub and that it compiles on CI and for other devs too? Or would it only work on my machine since it would reference a local path?

Would using Cocoapods with a private pod make things easier or would I run into the exact same problem of working on a local path dependency on my computer, but wanting to have it work for other devs too?

Or should I just go with a monorepo containing both apps and the shared code, and just have everything use relative paths? Would a SPM package inside of the monorepo be helpful in this situation, or just use relative paths in both apps?


Solution

  • I would recommend creating a (private) cocoapod for your business logic. Both your apps can refer to that cocoapod either via the release, somewhere in the repo or as a development pod, as needed.

    In order to avoid editing Podfile all the time, you can control your pod's source via external files and/or environment variables. Here's what I do in a couple of my projects:

    def library_pod
      src = ENV["POD_SOURCE"]
      if src == nil && File.file?(".pod_source")
        src = File.foreach(".pod_source").first.chomp
      end
      src = (src || "").split(":")
      case src[0]
      when "dev"
        # local dev pod, editable
        pod 'MyLibrary', :path => '../mylibrary', :inhibit_warnings => false
      when "head"
        pod 'MyLibrary', :git => 'https://github.com/mycompany/MyLibrary'
      when "branch"
        pod 'MyLibrary', :git => 'https://github.com/mycompany/MyLibrary', :branch=> src[1]
      else
        # default: use the release version 
        pod 'MyLibrary'
      end
    end
    
    target 'MyApp' do
      pod 'Pod1'
      pod 'Pod2'
      library_pod
    end
    

    where usually I have POD_SOURCE=dev in the environment on my dev machine. .pod_source contains release on master, and whatever branch name is appropriate in feature branches, so that my CI can to the right thing.