iOS工程文件整理

在 iOS 开发过程的当中不知道大家有没有遇到过这样的问题

  1. 文件的物理位置不对应
  2. 多余的未被工程引用的文件

这两个问题在工程变得比较大的时候,对工程的影响就比较让人难受了,于是我决定对工程进行整理。

因为我们的业务还是蛮复杂的,工程就特别大,手动查找和整理简直就是一场灾难,于是我决定使用脚本来帮助我完成这项虽然简单但是巨大的工程。

文件物理位置不对应的原因

说到这个问题,我们就需要提一下 project.pbxproj 文件了,对,就是那个每次合并冲突都让人头疼的文件。

project.pbxproj文件本质上是一种旧风格的 plist,因为可读性不是很好,逐渐被弃用,但是苹果却一直沿用,在这个文件当中包含了 Xcode 工程的各项配置和参数。project.pbxproj 文件采用 UDID 作为 key 索引来保证文件的唯一性,同时又在 value 当中指定其对应子节点的 UDID 值,这样就将一个树形结构的数据用列表形式表示了出来(UDID 是根据硬件和时间戳生成的唯一标识,这里不再详细解释其过程),工程的虚拟文件目录就是这么来的。

下面是一个转换成标准 plist 的结构,我们可以看到它的引用方式

20170731-1

因此我们解决物理位置不对应的问题只需要读取 project.pbxproj 的虚拟目录,并检查当前目录下是否有该文件,如果没有,到其对应的物理路径下,将其剪切过来即可。

针对第二个问题,我们可以扫描工程的整个物理目录,将每个文件去虚拟目录当中查找,如果没有被引用,直接删除即可。

脚本的编写

我们使用 python 来完成这次的任务,为什么不用 shell?因为 bash 3 不支持 key-value 结构,要升级到 bash 4 才可以,而 python 比较方便,就使用它了,推荐使用 python 3,因为它简化了蛮多操作的,比如编码什么的,不推荐使用预装的 python 2。

1.读取 project.pbxproj

我们先需要使用以下命令将 project.pbxproj 转换成标准的 plist,然后使用 python 库 biplist 读取

1
value=`plutil -convert xml1 -o - project.pbxproj` && echo $value >mPlistPath

2.检查虚拟目录下对应的文件是否存在,并进行相应操作

3.扫描工程物理路径,检查文件是否被引用,并进行操作

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# !/usr/bin/python
# coding=utf-8

from biplist import *
import os

global rootObjects
global rootArr
global plistPath

####################变量设置区#################################
rootPath= "/Users/yzq/Desktop/Test/Test"
projectFilePath = "/Users/yzq/Desktop/Test/Test.xcodeproj"
plistPath = "/Users/yzq/Desktop/123.plist"
##############################################################

def checkfile(filePath,filekey):
global rootObjects

dic = rootObjects[filekey]
# print(filekey)

strPath = ""
if "path" in dic:
strPath = dic["path"]
else:
strPath = dic["name"]

path = filePath + "/" + strPath
if "children" in dic:
if len(dic["children"]) < 1:
print("空文件夹"+"------"+path)
else:
for item in dic["children"]:
checkfile(path,item)
else:
if os.path.exists(path) == False:
print("文件不存在"+path)


def startCheckFile(mRootPath):
global rootObjects
global plistPath

plist = readPlist(plistPath)
rootObjects = plist["objects"]

for dic in rootObjects.values():
if ("name" in dic) and dic["name"] == "dfc_v3":
rootArr = dic["children"]
break

print(rootArr)

for item in rootArr:
print("开始检查item"+item+"-------------------------------")
checkfile(mRootPath,item)


def checkFileIsInProject(mPlistObject, mFileName, mFilePath):
for item in mPlistObject.values():
if (("path" in item) and (item["path"] == mFileName)) or (("name" in item) and (item["name"] == mFileName)):
# print("发现文件引用"+mFileName)
return
print("未发现文件应用:"+mFileName)
print(mFilePath)


def checkProject(mPlistObject, mFilePath):
dirList = os.listdir(mFilePath)
for item in dirList:
itemPath = os.path.join(mFilePath,item)
if os.path.isfile(itemPath):
checkFileIsInProject(mPlistObject,item,itemPath)
elif os.path.isdir(itemPath):
checkProject(mPlistObject,itemPath)
else:
print("当前路径出错:"+itemPath)


def startCheckProject(mRootPath):
global plistPath

plist = readPlist(plistPath)
rootObjects = plist["objects"]

checkProject(rootObjects,mRootPath)


def getPlist(mProjectFilePath,mPlistPath):
command = "rm -rf "+mPlistPath
os.system(command)
command = "value=`plutil -convert xml1 -o - "+mProjectFilePath+"/project.pbxproj` && echo $value >"+mPlistPath
os.system(command)
print("生成plist文件:"+mPlistPath)


print("这是一个帮你检查工程文件的脚本,请输入以下数字选项执行")
print("0.准备工作")
print("1.检查工程列表当中的文件是否在对应的物理位置")
print("2.检查工程当中是否有文件未加入引用")
choice = input("请输入:")
if choice == "0":
print("请设置脚本rootPath、projectFilePath、plistPath三个变量的路径值")
print("rootPath:工程根目录,dfc_v2的那个文件夹目录")
print("projectFilePath:工程文件目录,dfc_v2.xcodeproj文件目录")
print("plistPath:生成plist文件所在位置,该位置任意,具备读写权限即可")
elif choice == "1":
getPlist(projectFilePath,plistPath)
startCheckFile(rootPath)
elif choice == "2":
getPlist(projectFilePath,plistPath)
startCheckProject(rootPath)
else:
print("未输入正确的操作")

因为贸然的使用脚本改动工程有一定的风险,所以我在脚本当中把有问题的文件及其目录输出,手动去改动。

执行脚本

执行脚本,让后根据输出的结果修改工程,因为 Supporting Files 下的文件直接是在主目录下的,它下面的文件直接忽略即可。

1
python3 my.py