XCode 签名配置的快速设置工具

博主因修改了默认的“iPhone Developer”签名,导致每次真机测试都需要手动配置。为解决这一问题,作者利用Java编写了一个文本处理工具,通过读取并修改`project.pbxproj`文件中签名配置的部分,实现快速切换签名,简化了XCode项目的签名配置工作,提高了开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

               

之前看到过一篇无证做真机测试的文章,很受用~

不过因为当时手贱,把默认的 “iPhone Developer” 签名 改成了自己的名字

直接导致后来的每一个 XCode 项目,我想在真机上面看效果都要重新设置签名配置。


当然,是可以按照那篇文章把这个名字再修改回来,不过我当时懒,就一直这么弄着了

如今经过了那么长的时间,我电脑里面存储的很多工程都沿用了这蛋疼的配置

所以一时半会儿要修改过来,也是一件很费神的事情。


而且,最近我们团队有通过 Versions 做项目管理进行协作开发~

好不容易解决了多人提交冲突的问题,但是还是存在一些令人不满意的地方,

那就是签名的问题,我的另一个伙计真机测试的话是用的是他自己合法的签名

导致我更新或者他更新了以后,各自的签名被弄坏得重复的做一些签名的设置~


还有祸不单行,XCode 修改签名的时候有时修改不动,要关了再打开才能改的动,是个 bug~

自从上次观察过  project.pbxproj 文件以后,我就发现签名的配置数据也是以明文的方式保存在这个里面的~

那么,完全有可能用  Java 写一个文本处理工具将签名配置那一段做文本替换,置换为我常用的签名配置。


开始我想的使用  Java 的正则表达式,但是一路都不成功。

主要还是因为我对正则表达式的适用范围了解地还不够深刻~

正则表达式擅长于做文本的行内处理,那种跨多行文本的情况用正则是不能取得很好效果的

主要还是因为 Java 正则表达式的通配符 “.” 只能匹配除 \n 以外的所有其他字符 所致~


后来我看到签名配置那段的开头和结尾都做了固定写法的注释,

这样的话我便抛弃了正则表达式的解决方案,直接用 String.indexOfString() 来做实现了

接下来基本上就是很简单的事情了,几行代码便解决了所有问题,下面上代码:

RepairWonderPipe.java

package org.bruce.xcproj.codesign.repair;import java.io.File;import java.io.InputStream;/** * @author Bruce Yang * 仅限于处理 WonderPipe 工程(因为会将工程的某些设置抹掉)~ */public class RepairWonderPipe public static final String PROJECT_DIR = "/Users/user/SVN/WonderPipe";  /**  * 检查传入的文件目录是否为项目文件夹~  * @param dirProject  * @return  */ public static boolean isProjectDir(String strProjectDir) {  File dirProject = new File(strProjectDir);  if(dirProject.exists() && dirProject.isDirectory()) {   for(File fileItem : dirProject.listFiles()) {    if(fileItem.getName().endsWith(".xcodeproj")) {     return true;    }   }  }  System.out.println("不是项目文件夹~");  return false; }  /**  * @param args  */ public static void main(String[] args) {  // TODO Auto-generated method stub    // 1.检查是否为项目文件夹~  if(!isProjectDir(PROJECT_DIR)) {   System.err.println("不是 XCode 工程文件的根目录~");   return;  }    // 2.还是不确定能不能取到包下面的文件~  StringBuffer sbPath = new StringBuffer(PROJECT_DIR);  sbPath.append(File.separator).append("WonderPipe.xcodeproj");  sbPath.append(File.separator).append("project.pbxproj");  String strPath = sbPath.toString();    File f = new File(strPath);  if(!f.exists()) {   System.err.println("文件不存在: " + strPath);   return;  }    // 3.将文件内容读成字符串~  String fContents = StringFileBridge.file2String(f, "utf-8");//  System.out.println(fContents);    // 4.处理~  String strBegin = "/* Begin XCBuildConfiguration section */";  int iBeginIndex = fContents.indexOf(strBegin);    String strEnd = "/* End XCBuildConfiguration section */";  int iEndIndex = fContents.indexOf(strEnd) + strEnd.length();    String strToBeReplaced = fContents.substring(iBeginIndex, iEndIndex);//  System.err.println(strToBeReplaced);    String strResPath = "/org/bruce/xcproj/codesign/repair/XCBuildConfigurationBakup.txt";  InputStream is = RepairWonderPipe.class.getResourceAsStream(strResPath);  String strCfgTemplate = StringFileBridge.stream2String(is, "utf-8");//  System.err.println(strCfgTemplate);    String strOutput = fContents.replace(strToBeReplaced, strCfgTemplate);    // 5.将处理完毕的文件写回原文件~  StringFileBridge.string2File(strOutput, f);    //  test0();//  test1();//  test2(); }   /**  * 特殊字符 * 的匹配,前面加双反斜杠~  */ public static void test0() {  String str = "* sdfsdfsd *";  String reg = "\\* sdfsdfsd \\*";  if(str.matches(reg)) {   System.out.println("no!");  } }  public static void test1() {  String str = "/哈哈/";  String reg = "/.*/";  if(str.matches(reg)) {   System.out.println("哈哈");  } }  /**  * 结论:正则表达式不适合用于处理多行的文本~  */ public static void test2() {//  String str = "123123\n/* 嘻 */\n1231231";  String str = "\n/* 嘻 */\n";  String reg = "[.\n]*/\\* 嘻 \\*/[.\n]*";  if(str.matches(reg)) {   System.out.println("嘻");  } }}
StringFileBridge.java

package org.bruce.xcproj.codesign.repair;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileInputStream;import java.io.FileWriter;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.LineNumberReader;import java.io.Reader;import java.io.StringReader;import java.io.UnsupportedEncodingException;/** * 字符串与文件相互转换工具 * @author leizhimin 2009-7-14 15:54:18 */public class StringFileBridge /**  * 读取文件为一个内存字符串,保持文件原有的换行格式  * @param file 文件对象  * @param charset 文件字符集编码  * @return 文件内容的字符串  */ public static String file2String(File file, String charset) {  StringBuffer sb = new StringBuffer();  try {   FileInputStream fis = new FileInputStream(file);   InputStreamReader isr = new InputStreamReader(fis, charset);   BufferedReader br = new BufferedReader(isr);   LineNumberReader reader = new LineNumberReader(br);      String line;   while ((line = reader.readLine()) != null) {    sb.append(line).append(System.getProperty("line.separator"));   }   reader.close();  } catch (Exception e) {   e.printStackTrace();  }  return sb.toString(); }   /**  * @author Bruce Yang  * 该方法用于配合上面的方法使用。  * @param unicodeStr  * @return  */ public static String changeEncode(String unicodeStr, String charset) {  String utf8Str = null;  try {   utf8Str = new String(unicodeStr.getBytes(charset));  } catch (UnsupportedEncodingException e) {   e.printStackTrace();  }  return utf8Str; }   /**  * 将字符串存储为一个文件,当文件不存在时候,自动创建该文件,当文件已存在时候,重写文件的内容,特定情况下,还与操作系统的权限有关。  * @param text 字符串  * @param distFile 存储的目标文件  * @return 当存储正确无误时返回true,否则返回false  */ public static boolean string2File(String text, File distFile) {  if (!distFile.getParentFile().exists()) {   distFile.getParentFile().mkdirs();  }  BufferedReader br = null;  BufferedWriter bw = null;  boolean flag = true;  try {   br = new BufferedReader(new StringReader(text));   bw = new BufferedWriter(new FileWriter(distFile));   char buf[] = new char[1024 * 64]; // 字符缓冲区   int len;   while ((len = br.read(buf)) != -1) {    bw.write(buf, 0, len);   }   bw.flush();   br.close();   bw.close();  } catch (IOException e) {   flag = false;   e.printStackTrace();   System.out.println("将字符串写入文件发生异常!");  }  return flag; }  /**  * 文件转换为字符串  * @param in   字节流  * @param charset  文件的字符集  * @return    文件内容  */ public static String stream2String(InputStream in, String charset) {  StringBuffer sb = new StringBuffer();  try {   Reader reader = new InputStreamReader(in, charset);   int length = 0;   for (char[] c = new char[1024]; (length = reader.read(c)) != -1;) {    sb.append(c, 0, length);   }   reader.close();  } catch (Exception e) {   e.printStackTrace();  }  return sb.toString(); }   /**  * @param args  */ public static void main(String[] args) {  String x = file2String(new File("/Users/user/Desktop/123.java"), "GBK");  System.out.println(x);  boolean b = string2File(x, new File("/Users/user/Desktop/1234.java"));  System.out.println(b); }}

XCBuildConfigurationBakup.txt (PS: 和前面的两个类放在同一个 package 下面~)

/* Begin XCBuildConfiguration section */  BAAEAE4815A89BC600FF66D7 /* Debug */ = {   isa = XCBuildConfiguration;   buildSettings = {    ARCHS = "$(ARCHS_STANDARD_32_BIT)";    CODE_SIGN_ENTITLEMENTS = "";    CODE_SIGN_IDENTITY = "";    "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";    GCC_C_LANGUAGE_STANDARD = gnu99;    GCC_OPTIMIZATION_LEVEL = 0;    GCC_PREPROCESSOR_DEFINITIONS = (     DEBUG,     "COCOS2D_DEBUG=1",    );    GCC_SYMBOLS_PRIVATE_EXTERN = NO;    GCC_VERSION = com.apple.compilers.llvmgcc42;    GCC_WARN_ABOUT_RETURN_TYPE = YES;    GCC_WARN_UNUSED_VARIABLE = YES;    IPHONEOS_DEPLOYMENT_TARGET = 4.3;    "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";    SDKROOT = iphoneos;   };   name = Debug;  };  BAAEAE4915A89BC600FF66D7 /* Release */ = {   isa = XCBuildConfiguration;   buildSettings = {    ARCHS = "$(ARCHS_STANDARD_32_BIT)";    CODE_SIGN_ENTITLEMENTS = "";    CODE_SIGN_IDENTITY = "";    "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";    GCC_C_LANGUAGE_STANDARD = gnu99;    GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;    GCC_VERSION = com.apple.compilers.llvmgcc42;    GCC_WARN_ABOUT_RETURN_TYPE = YES;    GCC_WARN_UNUSED_VARIABLE = YES;    IPHONEOS_DEPLOYMENT_TARGET = 4.3;    OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";    "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";    SDKROOT = iphoneos;   };   name = Release;  };  BAAEAE4B15A89BC600FF66D7 /* Debug */ = {   isa = XCBuildConfiguration;   buildSettings = {    ALWAYS_SEARCH_USER_PATHS = YES;    CODE_SIGN_ENTITLEMENTS = Entitlements.plist;    CODE_SIGN_IDENTITY = yang3wei;    "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = yang3wei;    COPY_PHASE_STRIP = NO;    GCC_DYNAMIC_NO_PIC = NO;    GCC_PRECOMPILE_PREFIX_HEADER = YES;    GCC_PREFIX_HEADER = WonderPipe/Prefix.pch;    "GCC_THUMB_SUPPORT[arch=armv6]" = "";    GCC_VERSION = com.apple.compilers.llvmgcc42;    INFOPLIST_FILE = WonderPipe/Resources/Info.plist;    IPHONEOS_DEPLOYMENT_TARGET = 4.3;    OTHER_LDFLAGS = "-lz";    PRODUCT_NAME = "$(TARGET_NAME)";    PROVISIONING_PROFILE = "";    "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";    TARGETED_DEVICE_FAMILY = "1,2";    USER_HEADER_SEARCH_PATHS = "\"WonderPipe/libs\"";    WRAPPER_EXTENSION = app;   };   name = Debug;  };  BAAEAE4C15A89BC600FF66D7 /* Release */ = {   isa = XCBuildConfiguration;   buildSettings = {    ALWAYS_SEARCH_USER_PATHS = YES;    CODE_SIGN_ENTITLEMENTS = Entitlements.plist;    CODE_SIGN_IDENTITY = yang3wei;    "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = yang3wei;    COPY_PHASE_STRIP = YES;    GCC_PRECOMPILE_PREFIX_HEADER = YES;    GCC_PREFIX_HEADER = WonderPipe/Prefix.pch;    "GCC_THUMB_SUPPORT[arch=armv6]" = "";    GCC_VERSION = com.apple.compilers.llvmgcc42;    INFOPLIST_FILE = WonderPipe/Resources/Info.plist;    IPHONEOS_DEPLOYMENT_TARGET = 4.3;    OTHER_LDFLAGS = "-lz";    PRODUCT_NAME = "$(TARGET_NAME)";    PROVISIONING_PROFILE = "";    "PROVISIONING_PROFILE[sdk=iphoneos*]" = "";    TARGETED_DEVICE_FAMILY = "1,2";    USER_HEADER_SEARCH_PATHS = "\"WonderPipe/libs\"";    VALIDATE_PRODUCT = YES;    WRAPPER_EXTENSION = app;   };   name = Release;  };/* End XCBuildConfiguration section */

           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值