寻找沙盒之python篇

猪小花1号2018-08-23 17:11

做iOS开发的程序员,都经常需要查看自己的应用的沙盒目录, 本篇主要提供一个python工具,快速定位模拟器中应用的沙盒目录,包括AppExtension以及App Group目录。


前言

Xcode 8以前,苹果对插件基属于放养的状态,我们可以通过一定的方式,直接往 Xcode 注入代码。 对于Xcode插件的入门,可以看我的另一篇文章:《制作自己的Xcode插件》

到了Xcode 8,苹果为了避免类似 Xcode Ghost 事件的发生,禁用了插件, 然后开放了另一种形式:Source Editor extensions,但是这种形式局限性太大。能做的事情太少了。网上有方法可以去除Xcode的签名,这样使得 Xcode8 也可以继续使用插件了。感兴趣的可以搜一下update_xcode_plugins

由于签名被移除,所以它的安全性又重回 Xcode 7 时代,也就是还有可能受到类似 Xcode Ghost 的攻击。所以不推荐用改过的Xcode来提交应用到 Appstore

我之前也写了一个寻找沙盒的Xcode插件版,由于上面的原因,插件用着不方便,我就做了这个python简化版。


原理

模拟器的各项数据,被苹果存在了~/Library/Developer/CoreSimulator目录内,只需要分析这个文件夹的组成,就能快速定位到自己需要的目录。

首先看看一个完整的App沙盒路径:

~/Library/Developer/CoreSimulator/Devices/

    E6C54E8C-3EBD-4BDD-89AB-FF9B86FDDD5E/data/Containers/Data/Application/

        4B09C4D0-5C25-40B2-8084-B6AE0CA79A32/Documents

同理,我们还可以发现ExtensionApp Group也有自己的目录,分别是:

~/Library/Developer/CoreSimulator/Devices/
    E6C54E8C-3EBD-4BDD-89AB-FF9B86FDDD5E/data/Containers/Data/PluginKitPlugin/
        F3B497F6-1649-4972-8032-9065C572DBE2/Documents

~/Library/Developer/CoreSimulator/Devices/
    E6C54E8C-3EBD-4BDD-89AB-FF9B86FDDD5E/data/Containers/Shared/AppGroup/
        F022AD7A-ACD8-4EA7-990D-BE57E0BF0A1C/

是的,ExtensionApp Group也有自己的沙盒,程序员在开发这两个功能的时候,可以看看这两个目录,可以快速确定这两个的存储是否符合自己的预期。

可以看到,上面每个路径都有两个UUID,第一个UUID都是E6C54E8C-3EBD-4BDD-89AB-FF9B86FDDD5E,标识的是某台模拟器设备,比如iOS 10.2 的 iPhone 7

对于第二个UUID,标识的是对应的具体项目,比如4B09C4D0-5C25-40B2-8084-B6AE0CA79A32标识的是某个应用。层级结构非常的明显,问题在于怎么识别这些UUID。


探索与实现

设备UUID

~/Library/Developer/CoreSimulator/Devices/目录下,有两个pist文件.default_created.plistdevice_set.plist,内容差不多,后者信息更全一点,里面显示了每个UUID对应的设备型号,以及系统类型。

想要找到自己目前使用的设备的UUID,可以通过plist库,把这些数据读出来,然后指定对应的key。

allOSVersions = readPlist(devicesSetPath)['DefaultDevices']
devices = allOSVersions.get("com.apple.CoreSimulator.SimRuntime." + osVersion)
deviceId = allDevices.get("com.apple.CoreSimulator.SimDeviceType." + device)

每个~/Library/Developer/CoreSimulator/Devices/{UUID}/目录下都有一个plist,里面也有一个plist文件,包含了这个UUID所对应的型号与系统版本,可以用于反向查找。


应用UUID

应用UUID的获取,没有设备UUID方便,没有一个plist来统一显示每个应用对应的UUID。不过还好,这里的文件层级结构很明显,AppExtensionApp group分别放在对应的文件夹下了。虽然缺少一个统一的plist来查看UUID,但是和设备UUID一样,在每个应用UUID文件夹下同样有一个plist文件,里面包含了这个UUID对应的bundleId。

我们可以遍历所有的UUID,根据BundleId找到我们需要的那个应用。

def getBundles(deviceDataPath, bundleId):
  for appId in os.listdir(deviceDataPath) :
    appPlist = os.path.join(deviceDataPath, appId, ".com.apple.mobile_container_manager.metadata.plist")
    if os.path.isfile(appPlist) == False:
      continue
    appPlistDic = readPlist(appPlist)
    plistBundleId = appPlistDic["MCMMetadataIdentifier"]
    if plistBundleId == bundleId:
      return os.path.join(deviceDataPath, appId)
  return None


使用

python库

本脚本使用了两个工具argparsebiplist,没有安装过的可以通过以下命令安装:

pip install argparse
pip install biplist
或
sudo easy_install argparse
sudo easy_install biplist


命令行参数

命令行参数工具,使用了现成的库argparse,支持4个参数

➜ ./gotoDoc.py -h
usage: gotoDoc.py [-h] [-t TYPE] [-o OSVERSION] [-d DEVICE] [-b BUNDLEID]

 Go to simulator documents, bundleId support partial input.

optional arguments:
  -h, --help    show this help message and exit
  -t TYPE       type: `app`,`extension`,`group`
  -o OSVERSION  osVersion: iOS-10-2
  -d DEVICE     device: iPhone-7
  -b BUNDLEID   bundleId: `com.hzch.gotoDoc`

t意为type,表示需要跳转的类型,不加默认为app

o意为osVersion,表示系统版本,不加默认为iOS-10-2

d意为device,表示设备型号,不加默认为iPhone-7

b意为bundleId,不加的话会根据type而使用不同默认值。

以上默认值均可以在脚本的开头指定好。

DEFAULTTYPE = "app"
DEFAULTOS = "iOS-10-2"
DEFAULTDEVICE = "iPhone-7"
DEFAULTBUNDLEIDAPP = "com.hzch.gotoDoc"
DEFAULTBUNDLEIDEXTENSION = "com.hzch.gotoDoc.shareExtension"
DEFAULTBUNDLEIDGROUP = "group.com.hzch.gotoDoc"


错误提示

一切配置正常的情况下,通常只需要使用默认参数就可以直接打开对应的文件夹了。但是有的时候,我们需要跳转到非默认路径,这个时候需要输入参数,但是参数的具体值忘记的怎么办呢,比如系统版本很可能就会错误输入,比如ios10.2

当脚本在没有找到对应的信息是,会自动把相关的信息自动打印在控制台,第二次输入基本上就不成问题了。

➜ ./gotoDoc.py -o ios10.2
[I] Open: type:app
[E] Version error!
[I] All os version:
====================
watchOS-2-2
tvOS-9-2
tvOS-10-0
tvOS-10-1
iOS-9-3
watchOS-3-1
watchOS-3-0
iOS-10-0
iOS-10-1
iOS-10-2
====================

另外由于bundleId一般比较长,而且不好记,所以bundleId支持模糊匹配,当模糊匹配项多于一条时,也会在控制台中输出。


Zsh

Zsh 是一款功能强大终端(shell)软件,推荐使用。通过Zsh,可以更快的打开对应的沙盒。比如,通常我们的命令行停留在项目的根目录,笔者通常使用../python/gotoDoc.py来打开沙盒。使用Zsh,只需要输入..+ 就可以找到历史命令,快速执行了。

当然Zsh的功能很强大,不当当只有这么点,大家自行使用。

另外也很多其他的方法可以快速执行脚本,比如定义快捷命令,大家自行研究。


完整代码

由于不能这里不支持传.py类型的文件,在文件后加了.txt,下的时候可以直接把.txt删除。

如果出现permission denied,可以加一下权限chmod +x gotoDoc.py

gotoDoc.py.txt

python经验较少,欢迎吐槽。


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者江春辉授权发布。