Desenvolvimento

5 out, 2017

Adeus Xcode project.pbxproj Merge!

Publicidade

Este artigo foi originalmente publicado (em inglês) no Cocoacademy. Veja aqui.

***

Há um tempo, decidi resolver um problema que me incomodava muito: o merge do project.pbxproj no Xcode. Todo mundo que trabalhou como desenvolvedor iOS sabe quanta frustração esse único arquivo pode produzir. Ele é o responsável por quase tudo o que acontece no seu projeto Xcode: todas as mudanças, todas as configurações diferentes, cada remoção ou adição que um desenvolvedor faz, afeta esse arquivo todo-poderoso.

Não é nenhum segredo que esses tipos de merge acontecem muito e, para piorar a situação, geralmente não são do tipo fácil. Não estou dizendo que são difíceis, mas dão uma verdadeira dor de cabeça na maioria das vezes.

Então, o que nós podemos fazer sobre isso?

Para responder essa pergunta, testei pela primeira vez o Xcake, uma gem ruby que endereça o problema.

Da página do Xcake no Github:

Você descreve seu projeto em um arquivo de texto simples: seu Cakefile. O Xcake cria seu projeto com base nesse arquivo, incluindo a importação de todos os seus arquivos e a criação de grupos para coincidir com o hierarquia definida no Cakefile.

Isso é perfeito para trabalhar em equipe ou com CocoaPods, pois reduz os conflitos, facilita a modificação de configurações e permite que você crie um projeto limpo sempre que precisar.

Então, basicamente, você pode criar um Cakefile, arquivo que descreve a configuração do seu projeto. Depois disso, a única coisa que precisa fazer é executar um simples cmd:

xcake make

Voilà!! Um novo projeto Xcode. No entanto, você poderia ter obtido esta informação no GitHub. Então vou tentar tornar este post um pouco mais interessante compartilhando algumas das coisas que fiz durante a migração do meu projeto da Marvel para Xcake, bem como alguns dos benefícios de fazê-lo.

Cake primeiro, certo?

Primeiro precisamos criar um Cakefile, usando o cmd:

xcake init

Depois você só precisa configurar como quiser, mas compartilho a configuração do meu logo abaixo. A página GitHub do Xcake tem mais alguns exemplos de diferentes configurações.

# just helper variables to use these values in a consistent way across whole file

currentSwiftVersion = "3.1.0"



# Change this to set a different Project file name
project.name = "Marvel"

# Replace this with your class prefix for Objective-C files.
# project.class_prefix = "APP"

project.organization = "TPLioy"

# By default Xcake defaults to creating the standard Debug and Release
# configurations, uncomment these lines to add your own.
#
#debug_configuration :Staging
#debug_configuration :Debug
#release_configuration :Release

# Change these to the platform you wish to support (ios, osx) and the
# version of that platform (8.0, 9.0, 10.10, 10.11)
#
application_for :ios, 10.0 do |target|

    target.language = :swift

    #Update these with the details of your app
    target.name = "Marvel"
    target.all_configurations.each { |c| c.product_bundle_identifier = "com.tpioy.marvelapp"}

    # Uncomment to target iPhone devices only
    #
    # File patterns can be seen here https://guides.cocoapods.org/syntax/podspec.html#group_file_patterns
    #
    target.all_configurations.each { |c| c.supported_devices = :iphone_only}

    # Uncomment this to include additional files
    #
    #target.include_files << "FolderToInclude/*.*"

    # Uncomment this to exclude additional files
    #
    # File patterns can be seen here https://guides.cocoapods.org/syntax/podspec.html#group_file_patterns
    #
    #target.exclude_files << "ExcludeToInclude/*.*"

    # Uncomment to set your own build settings
    #
    target.all_configurations.each do |c|
      c.settings["SWIFT_VERSION"] = currentSwiftVersion
      c.settings["INFOPLIST_FILE"] = "Marvel/Resources/Info.plist"
    end
    #target.all_configurations.each { |c| c.settings["GCC_PREFIX_HEADER"] = "APP-Prefix-Header.pch"}
    #target.all_configurations.each { |c| c.settings["SWIFT_OBJC_BRIDGING_HEADER"] = "APP-Bridging-Header.h"}

    # Uncomment to define your own preprocessor macros
    #
    #target.all_configurations.each { |c| c.preprocessor_definitions["API_ENDPOINT"] = "https://example.org".to_obj_c}

    #Configure schemes
    target.scheme(target.name) do |scheme|
      # Defaults Values
      # scheme.test_configuration = :debug
      # scheme.launch_configuration = :debug
      # scheme.archive_configuration = :release
    end

    # Configure Build phases
    target.shell_script_build_phase "[CP] Check Pods Manifest.lock", <<-SCRIPT
      diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
      if [ $? != 0 ] ; then
          # print error to STDERR
          echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
          exit 1
      fi
    SCRIPT

    target.shell_script_build_phase "[CP] Embed Pods Frameworks", <<-SCRIPT
      "${SRCROOT}/Pods/Target Support Files/Pods-Marvel/Pods-Marvel-frameworks.sh"
    SCRIPT

    target.shell_script_build_phase "[CP] Copy Pods Resources", <<-SCRIPT
      "${SRCROOT}/Pods/Target Support Files/Pods-Marvel/Pods-Marvel-resources.sh"
    SCRIPT

    target.shell_script_build_phase "SwiftGen Images", <<-SCRIPT
      "${PODS_ROOT}/SwiftGen/bin/swiftgen" images -t swift3 "${SRCROOT}/Marvel/Resources/Assets.xcassets" --output "${SOURCE_ROOT}/Marvel/Resources/ImageAssets.swift" --enumName ImageAssets
    SCRIPT

    # Comment to remove Unit Tests for your app
    #
    unit_tests_for target do |test_target|

        test_target.name = "MarvelTests"
        test_target.include_files = ["MarvelTests/**/*.*"]

        test_target.all_configurations.each do |c|
          c.settings["SWIFT_VERSION"] = currentSwiftVersion
        end
        # configure any other target-related properties
        # as you would do with application target
    end

    # Uncomment to create a Watch App for your application.
    #
    #watch_app_for target, 2.0
end

Como você pode ver, a configuração é bastante direta. Você pode vincular arquivos de script diferentes para suas fases de script, bem como muitas outras coisas, variando caso a caso. Eu decidi manter as coisas simples para esse post e o Cakefile nos permite parar de versionar nosso * .xcodeproj e deixar o Xcake gerá-lo desde o início, toda vez, o que reduz muito os conflitos e problemas de merge.

So far so good?

O único problema que encontrei nessa abordagem até agora é que o Cakefile tem suporte limitado para esquemas, portanto não é possível personalizar muito depois de gerar. Depois de abrir uma issue na página do Github, me sugeriram uma solução que lida com esse problema, pelo menos por enquanto. A solução apenas substitui o esquema por um salvo após a geração do projeto Xcode.Bootstrapping.

Eu não sei o que vocês acham, mas essas etapas pedem um script de inicialização. E é isso que vamos fazer com a ajuda do Fastlane.

desc "Bootstrap"
lane :bootstrap do
xcake
sh "cp -r ../schemes/Marvel.xcscheme ../Marvel.xcodeproj/xcshareddata/xcschemes/"
cocoapods
end

Depois de criar essa pista de Bootstrap, só precisamos adicionar o plugin Xcake ao Fastlane usando o cmd abaixo:

fastlane add_plugin xcake

Última parte: agora, cada vez que puxar seu código, você só precisa executar:

fastlane bootstrap

…and you’re good to go!

Ainda preciso explorar um pouco mais essa ferramenta e testá-la em um projeto real em produção para ver como isso funciona, mas até agora gostei bastante. Se ela corrigir 80% dos problemas gerados por project.pbxproj, eu ficaria mais do que feliz. E vocês? Este pbx é um problema no seu projeto? Eu adoraria ouvir o que você acha sobre essas e outras ferramentas que abordam o problema e/ou sua experiência com esse tipo de coisa.

Como sempre, qualquer sugestão, dúvidas ou feedbacks são mais do que bem-vindos.

***

Este artigo foi publicado originalmente em: https://www.concrete.com.br/2017/09/13/farewell-xcode-project-pbxproj-merge/