Xcode 8 工程的一些整理

最近对我Xcode中的工程进行了整理,这不Xcode 9也要出来了,先总结一下做个记录。

做这些事,是为了解决我目前遇到的问题。简单描述下问题,一个工程,里面有多个target(20+而且还会增长),每个发行的App都用对应的target打包。这些App,bundle id不同,icon不同,launch screen不同,以及连接的服务器不同,有个别的几个由于特殊的需求,代码中有宏进行分支。这些icon,launch screen,和服务器等配置都是通过别的方式动态提供,并不放在我工程的源码里(因为每个target都不同,且随时可能发生变化,我的源码不需要维护这个变化)。

我原来的方法是完成下面几步:
1,在xcode中depulicate一个模板target;
2,获取这个新target对应的icon等配置;
3,menu中File->Add将Assets,launchScreen.storyboard,plist等文件都添加给这个target;
4,Podfile中加入这个target,并执行pod install
这样的方法,很麻烦,步骤多,容易错,而且,当需求比较密集的时候,我真的是很烦这种重复的手工劳动。

不得已,我必须改变现在工程配置的方式,现在问题已经完美解决,整个工程里,只有一个target,配合一个脚本,可以进行茫茫多target的管理了。

废话不多说,下面是这次实践中的一些关键点的总结:
1,Cocoa Pods 中,多个target依赖相同的第三方库,Podfile文件的写法:

abstract_target 'defaults' do
    platform:ios,'8.0'

   # Podfile是Ruby脚本,此处列出所有需要使用第三方库依赖的target
    targetsList = ['target1', 'target2']
    targetsList.each do |t|
        target t do
           # 这些target需要依赖的第三方库
            pod 'AFNetworking'
        end
    end
end

或者也可以写成这样,方便对个别target进行单独依赖库配置:

abstract_target 'defaults' do
    platform:ios,'8.0'

    pod 'AFNetworking'

    targetsList = ['target1', 'target2']
    targetsList.each do |t|
        target t do
              pod 'XXXXX'
        end
    end
end

然后在控制台执行:

pod install

生成静态库自动加入到target中,可以通过TARGETS->General->Linked Frameworks and Libraries 中查看被加入文件libPods-default-target1.a

2,对工程文件的修改
工程文件位于MyProject/MyProject.xcodeproj中。右键->显示包内容,可以看到里面的文件project.pbxproj。这个文件就是xcode的工程文件,可以用编辑器打开。注意到其中的编译configuration:

xxxxxyyyyyzzzzz /* Debug */ = {
            isa = XCBuildConfiguration;
            baseConfigurationReference = aaaaabbbbbbcccc /* Pods-defaults-target1.debug.xcconfig */;
            buildSettings = {
                ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
                CODE_SIGN_ENTITLEMENTS = MyProject/target1.entitlements;
                CODE_SIGN_IDENTITY = "iPhone Developer";
                "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
                DEVELOPMENT_TEAM = XXXXXXXX;
                ENABLE_BITCODE = NO;
                HEADER_SEARCH_PATHS = "$(inherited)";
                INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
                IPHONEOS_DEPLOYMENT_TARGET = 8.0;
                LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
                LIBRARY_SEARCH_PATHS = "$(inherited)";
                OTHER_CFLAGS = "";
                OTHER_LDFLAGS = (
                    "$(inherited)",
                    "-ObjC",
                    "-framework",
                    "\"AVFoundation\"",
                    "-framework",
                    "\"UIKit\"",
                    "-all_load",
                );
                PRODUCT_BUNDLE_IDENTIFIER = "com.myproject.target-1";
                PRODUCT_NAME = "$(TARGET_NAME)";
                PROVISIONING_PROFILE = "";
            };
            name = Debug;
        };

以上是工程文件中的其中一段,是编译Debug版本时使用的配置:

# App icon使用的assets文件,在TARGETS->General->App Icons and Launch Images中选中的值
ASSETCATALOG_COMPILER_APPICON_NAME 

# Launch Images,同上
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME

# 签名文件,如果有push这种需要单独签名的功能,在Capabilities中打开push开关后,即生成一个entitlements文件,与target同名。如果target要使用另外的entitlement文件,在Xcode中配置即可,配置路径TARGETS->Build Settings->Signing->Code Signing Entitlements
CODE_SIGN_ENTITLEMENTS

# 开发者ID,TARGETS->General->Signing中配置的开发者
DEVELOPMENT_TEAM

# target使用的Info.plist,可以放在其他路径。加入方式是在menu->File->Add添加plist文件进工程,然后将target正在使用的plist文件删掉,这时Xcode->TARGETS->General会提示没有plist,点击button,在弹出的列表中选择刚才加入的plist文件即可。选中后,General中会列出plist中的信息,而且与plist有关的文件的path也会被同步更新,不需要手动进行修改。
INFOPLIST_FILE

# 编译选项,可以加入宏控制语句,写法为-DXXXXX,在代码中就可以使用 #ifdef XXXXX #endif 或者 #define #endif。与C相同。
OTHER_CFLAGS

# link选项,一般使用pod第三方库后,会自动被加入一些链接选项
OTHER_LDFLAGS

# App bundle ID,在TARGETS->General中显示的Bundle Identifier。Xcode 8中,Info.plist中对应的字段为:
# <key>CFBundleIdentifier</key>
# <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
# 即plist中使用project文件中的这个字段,所以如果要修改bundle id,最好是通过project文件修改此字段,而不要直接修改plist文件
PRODUCT_BUNDLE_IDENTIFIER

我现在的管理工程和打包指定target的方法就是,在终端运行一个我自己写的脚本,主要动作是:
1,获取指定target的配置包(包含icon,launch screen,plist等),plist中的display name每个target不同,其它内容都相同;
2,把配置包中的文件都拷贝到相应的路径,确保project文件能找到这些文件(如果找不到,在打开xcode工程后,就开始报错,提示找不到文件);
3,替换project文件中的PRODUCT_BUNDLE_IDENTIFER, 我的经验是,不要用编辑器打开后,手动替换,那样保存后,project可能就不能用了(没有具体研究,猜想是编码等的问题),使用awk和sed进行替换,bash脚本如下:

BUNDLE_ID=com.myproject.target-x

# 从工程文件 MyProject.xcodeproj/project.pbxproj 中获取旧的bundle id
OLD_BUNDLE_ID=$(awk -F '=' '/PRODUCT_BUNDLE_IDENTIFIER/ {print $2; exit}' MyProject.xcodeproj/project.pbxproj | awk -F'"' '{print $2}')

# 替换,注意Mac上sed -i 后需要跟一个空串"",而且如果是在脚本中,后面最好使用双引号(在控制台上测试语句时用单引号没问题,但是在脚本中也用单引号就不行了,必须双引号)
sed -i "" "s/${OLD_BUNDLE_ID}/${BUNDLE_ID}/g" MyProject.xcodeproj/project.pbxproj

# 获取新的bundle id
NEW_BUNDLE_ID=$(awk -F '=' '/PRODUCT_BUNDLE_IDENTIFIER/ {print $2; exit}' MyProject.xcodeproj/project.pbxproj | awk -F'"' '{print $2}')

4,Xcode中的scheme是在打开Xcode时自动创建的(auto create schemes),但是,如果是从SVN中新check out出来的代码,不打开Xcode工程,要用fastlane gym, xcodebuild等工具直接编译,尤其在有workspace,编译时需要提供的是scheme而不是target的情况下,新拿回来的工程中没有scheme,必须要打开一次Xcode生成scheme么?答案是,可以自己用Ruby生成一下scheme。以下是检查有没有scheme,如果没有就recreate的脚本,包含一个bash和一个ruby:

# bash,用于检查project中是否有所需的scheme

#!/bin/bash

# 此脚本用于检查工程中是否包含指定scheme
# 参数: 
# $1: 指定的scheme,待检查的scheme
# $2: 使用的用户
# 返回值: 0 找到scheme; -1 未找到

SCHEME=$1
USER=$2

function check_schemes {
    scheme_exist=0
    ALL_TARGETS_AND_SHCHEMS=$(sudo -u ${USER} xcodebuild -list -project MyProject.xcodeproj)
    KEY_STRING="Schemes:"
    CONTAIN_SCHEMES=$(echo ${ALL_TARGETS_AND_SHCHEMS} | grep ${KEY_STRING})
    
    if [ -z "${CONTAIN_SCHEMES}" ]; then
        echo "此工程没有Schemes"
    else
        echo "找到全部Schemes"  
        ALL_SCHEMES=($(echo ${ALL_TARGETS_AND_SHCHEMS##*${KEY_STRING}}))        
        for one_scheme in "${ALL_SCHEMES[@]}"; do
        if [ "${one_scheme}" == "${SCHEME}" ]; then
            scheme_exist=1
            break
        fi
    done
    fi
    return ${scheme_exist}
}

check_schemes

RESULT=$?
if [ ${RESULT} -eq 0 ];then
    # 没有找到对应scheme, 重新生成schemes
    echo "没有找到对应的scheme: ${SCHEME}, 重新生成全部schemes"  
    ruby RecreateSchemes.rb
else
    echo "找到对应的scheme: ${SCHEME}"  
    exit 0
fi

# 重新生成schemes后再次检查
check_schemes

RESULT=$?
if [ ${RESULT} -eq 0 ];then
    # 没有找到对应的schemes
    echo "不存在scheme: ${SCHEME}"  
    exit -1
fi

Ruby 文件, 用于重新生成schemes。

# RecreateSchemes.rb

#!/usr/bin/env ruby

require 'xcodeproj'

xcproj = Xcodeproj::Project.open("MyProject.xcodeproj")
xcproj.recreate_user_schemes
xcproj.save

可以在build之前先使用上面的脚本检查是否有可以编译的scheme。

5,针对个别需要在编译时加入CFLAG进行条件控制的target,可以使用下面的编译选项:

# 1) 使用xcodebuild进行编译
xcodebuild -project MyProject.xcodeproj -scheme target1 OTHER_CFLAGS='${inherited} -DTARGET1=1'

########
# 输出包含:
Build settings from command line:
    OTHER_CFLAGS = ${inherited} -DTARGET1 =1
export OTHER_CFLAGS=" -DTARGET1 =1"
# 可见${inherited}为空
# 注意:OTHER_CFLAGS的参数需要有单引号
# 2) 使用fastlane gym,代码中使用方式是 #if TARGET1 #endif ,通过设置-DTARGET1 =1或-DTARGET1 =0进行条件编译
fastlane gym --workspace MyProject.xcworkspace --scheme target1 --clean --configuration Release --archive_path ~/Desktop/temp/target1 --export_method enterprise --output_directory ~/Desktop/temp --output_name target1-xxx.ipa --xcargs OTHER_CFLAGS="'${inherited} -DTARGET1 =1'"

#########
# 输出包含:
$ xcodebuild -list -workspace MyProject.xcworkspace -configuration Release
$ xcodebuild -showBuildSettings -workspace MyProject.xcworkspace -scheme target1 -configuration Release
+------------------+---------------------------------------------------------+
|                           Summary for gym 2.57.0                           |
+------------------+---------------------------------------------------------+
| workspace        | MyProject.xcworkspace                                        |
| scheme           | target1                                                 |
| clean            | true                                                    |
| configuration    | Release                                                 |
| archive_path     | /Users/xxx/Desktop/temp/target1                           |
| export_method    | enterprise                                              |
| output_directory | /Users/xxx/Desktop/temp                                |
| output_name      | target1-xxx                                             |
| xcargs           | OTHER_CFLAGS=' -DTARGET1 =1'                        |
| destination      | generic/platform=iOS                                    |
| build_path       | /Users/xxx/Library/Developer/Xcode/Archives/2017-09-21 |
| silent           | false                                                   |
| skip_package_ipa | false                                                   |
| buildlog_path    | ~/Library/Logs/gym                                      |
| xcode_path       | /Applications/Xcode.app                                 |
+------------------+---------------------------------------------------------+

[11:30:00]: $ set -o pipefail && xcodebuild -workspace MyProject.xcworkspace -scheme target1 -configuration Release -destination 'generic/platform=iOS' -archivePath /Users/xxx/Desktop/temp/target1.xcarchive OTHER_CFLAGS=' -DTARGET1 =1' clean archive | tee /Users/xxx/Library/Logs/gym/target1-target1.log | xcpretty

# 可见fastlane最后在xcodebuild中的CFLAGS参数需要单引号才能正常执行
# 3) 或者,代码中使用方式是 #ifdef  TARGET1   #else  #endif
fastlane gym --workspace  MyProject.xcworkspace --scheme target1 --clean --configuration Release --archive_path ~/Desktop/temp/target1 --export_method enterprise --output_directory ~/Desktop/temp --output_name target1-xxx.ipa --xcargs OTHER_CFLAGS="'${inherited} -DTARGET1'"

注意:
1)${inherited}或者写为${value},是继承工程中配置的CFLAGS,如果没有则为空串;
2)引号的使用,如果使用xcodebuild,直接使用单引号括住CFLAGS;如果使用fastlane gym则需要在单引号外再加上双引号,保证传入fastlane中调用的xcodebuild时,依然OTHER_CFLAGS后参数有单引号。

6,最后是关于在member center上申请push证书和provisioning profile的。一直以来的操作是先在Xcode上建好target,填好新的bundle后,一选signing,自动就在member center对应帐号的App ID中创建出来了。这次反着来,在member center直接创建对应的App ID,申请push证书和profile,下载profile安装。然后再直接用脚本替换project文件中的BUNDLE_ID,直接做出对应的app。这样,每次添加一个新的target(即对应添加一个新的app)只需要在member center中进行设置-配置-下载证书-安装证书,代码和工程不需要发生任何改变,也不需要因为新添加了target而提交代码了:)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别...
    吃瓜群众呀阅读 11,619评论 3 42
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 【转载】曾梦想仗剑走天涯 1.Xcode IDE概览 说明:从左到右,依次是“导航窗格(Navigator)->边...
    06a6a973d7ab阅读 3,781评论 2 20
  • 因为要结局swift3.0中引用snapKit的问题,看到一篇介绍Xcode8,swift3变化的文章,觉得很详细...
    uniapp阅读 4,299评论 0 12
  • 难以置信的言表,恰似初夏之轻酥。 从春日里走了出来,夏日如此养目。 忘了曾经的年少,忘不了曾经的仰慕。 从你的心中...
    徐達開阅读 317评论 0 4