XcodeGen으로 협업하기

Xcode

Featured image

@

혼자 개발할 땐, 크게 신경쓰지 않지만 협업을 하다보면 문제가 생기는 곳이 있다. 첫 번째론 .xcodeproj와 .storyboard 이다. 이 중에서 .xcodeproj의 충돌을 막는 방법에 대해서 이야기해보고자 한다.

XcodeGen

XcodeGen의 핵심적인 개념은 “.xcodeproj에서 폴더의 위치, 파일 수정, settings, phases 등이 일관적이지 않게 변경되어서 충돌이 발생하니, 깃에 추가하지 말고, 각자 갖고 있자. 필요한 설정은 project.yml에 저장하고 보기 쉽게 관리하자.” 이다.

자세한 설치방법은 Github Readme을 참고하기 바란다. 참고한 방법대로 설치하고 적용하고자 하는 프로젝트에 project.yml 파일을 생성한다.

자동으로 yml파일이 생성되는 것이 아니라서 하나 씩 적용하고 싶은 프로젝트의 설정에 따라 변경해줘야 한다.

생성했다면 아래의 예제를 보면서 project.yml파일을 개인에 맞게 설정하길 바란다.

name: ${프로젝트 이름}
options:
    createIntermediateGroups: true #path로 그룹핑
    groupSortPosition: top # sort 기준
    postGenCommand: pod install # 생성 이후 명령 실행, cocoapods이 brew로 설치되어 있어야 제대로 작동.
configs: # configuration 별 설정
    Debug: debug
    Release: release
configFiles:
    Debug: xcconfig/Project-Debug.xcconfig
    Release: xcconfig/Project-Release.xcconfig
packages:
    RxSwift:
        url: https://github.com/ReactiveX/RxSwift
        from: 5.1.1
    RxAlamofire:
        url: https://github.com/RxSwiftCommunity/RxAlamofire
        from: 5.6.2
targets:
    ${타겟 이름}:
        platform: iOS
        type: application
        sources:
            - ${소스 이름} # 대부분 프로젝트 이름과 동일하더라...
        configFiles:
            Debug: xcconfig/${프로젝트 이름}-Debug.xcconfig
            Release: xcconfig/${프로젝트 이름}-Release.xcconfig
        dependencies:
            - package: RxSwift
            - package: RxAlamofire

xcconfig라는 게 뜬금없이 생겨났다. 해당 파일을 만드는 방법은 BuildSettingExtractor에서 릴리즈된 .dmg 파일을 받아서 설치 후 추출하면 된다. 이렇게 만든 후, 프로젝트 폴더에서 아래의 명령어를 호출하면 .xcodeproj 파일을 대신 생성해줄 것이다.

xcodegen

위의 project.yml 파일로 해결이 된다면 최근에 만들어진 프로젝트이거나, 작은 프로젝트일 가능성이 높다. 대부분 회사 프로젝트는 오래되고 여러가지 설정들이 들어 있어 위의 설정으론 빌드가 되지 않을 것이다.

name: ${프로젝트 이름}
options:
    createIntermediateGroups: true #path로 그룹핑
    groupSortPosition: top # sort 기준
    postGenCommand: pod update # 생성 이후 명령 실행, cocoapods이 brew로 설치되어 있어야 제대로 작동.
configs: # configuration 별 설정
    Debug: debug
    Release: release
configFiles:
    Debug: xcconfig/Project-Debug.xcconfig
    Release: xcconfig/Project-Release.xcconfig
packages:
    RxSwift:
        url: https://github.com/ReactiveX/RxSwift
        from: 5.1.1
    RxAlamofire:
        url: https://github.com/RxSwiftCommunity/RxAlamofire
        from: 5.6.2
    Kingfisher:
        url: https://github.com/onevcat/Kingfisher
        from: 5.13.1
    HalfPieView:
        path: vendor/HalfPieView
targets:
    jobplanet:
        platform: iOS
        type: application
        scheme:
            commandLineArguments:
                "-FIRDebugEnabled": true
            environmentVariables:
                OS_ACTIVITY_MODE: disable
        sources:
            - path: ${flag가 필요한 파일의 위치}
              compilerFlags: "-fno-objc-arc"
            - ${소스 이름} # 대부분 프로젝트 이름과 동일하더라...
        settings:
            CLANG_ENABLE_OBJC_WEAK: false
            CLANG_WARN_DOCUMENTATION_COMMENTS: false
            CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF: false
            SWIFT_COMPILATION_MODE: Incremental
        configFiles:
          Debug: xcconfig/${프로젝트 이름}-Debug.xcconfig
          Release: xcconfig/${프로젝트 이름}-Release.xcconfig
        preBuildScripts:
            - name: Run Script
              script: |
                # 빌드 시 스크립트가 작동합니다.
        dependencies:
            - package: RxSwift
            - package: RxAlamofire
            - package: Kingfisher
            - package: HalfPieView
            - sdk: AdSupport.framework
              embed: false
            - sdk: AudioToolbox.framework
              embed: false
            - sdk: AVFoundation.framework
              embed: false
            - sdk: CFNetwork.framework
              embed: false
            - sdk: CoreData.framework
              embed: false
            - sdk: CoreServices.framework
              embed: false
            - sdk: JavaScriptCore.framework
              embed: false
            - sdk: StoreKit.framework
              embed: false
            - sdk: SystemConfiguration.framework
              embed: false
            - sdk: libsqlite3.0.tbd
            - sdk: libxml2.tbd
            - sdk: libz.tbd

위의 예제는 좀 더 많은 설정들이 포함되어 있다. 상단부터 차례로 설정하자면

  1. options에서 설정을 한 후 pod update를 진행한다. cocoapods이 brew로 설치되어 있어야 제대로 작동하니 참고
  2. packages -> HalfPieView는 내부 SPM 파일이다. path를 지정해주고 맨 밑에 dependencies에 추가해서 적용할 수 있다.
  3. scheme 설정도 가능하고 sources에서 특정 파일만 compilerFlags이 필요한 경우 따로 설정할 수 있고, 맨 먼저 호출을 해야 뒤에 나오는 - ${소스 이름}에 덮어씌워지지 않는다. (순서가 중요)
  4. settings는 project - settings의 값들인데, .xcodeproj을 우클릭 후 패키지 내용보기에서 project.pbxproj에 작성되어 있는 key값이다. 해당 값을 설정할 수 있다.

이것 외에 더 다양한 설정들이 존재한다. 만약 본인의 프로젝트의 설정이 부족하다면 가이드 문서를 참고하면서 설정을 추가 해야한다. 친절하진 않지만 계속 보다보면 익숙해져서 필요한 설정 값을 가져올 수 있을 것이다.

정 모르겠으면 이슈에 질문을 남겨보자~

한 가지 생길 수 있는 버그 중 하나는 sources에 .a 나 .framework의 파일들을 갖고 있다면 Build Phases에 모두 복사되어 버린다. 이렇게 되면 앱 실행엔 문제가 없지만 배포할 때 symbol Error가 발생하니 복사되는 파일은 제거하거나 내부 spm으로 처리하거나 따로 분리해둬야한다.

위의 방법으로 협업에 문제가 되던 .xcodeproj를 제거했다. 다음엔 또 다른 협업하는 방법인 Github Action을 소개하겠다. 그 때까지 바이바이~

ps. xcodegen 명령어를 XcodeBehaviors에 저장해두고 사용하면 편하다.

#!/bin/bash

# move project dir
PROJECT_HOME=`pwd`
echo "cd $PROJECT_HOME" > /tmp/tmp.sh

# pod init & update
echo "xcodegen" >> /tmp/tmp.sh

chmod +x /tmp/tmp.sh
open -a Terminal /tmp/tmp.sh