Ruby
Xcode

[XCode][iOS] sqliteのファイルパスを簡単に探すためのスクリプト

More than 1 year has passed since last update.


環境

下記環境でiOS開発している。


  • XCode7


問題点

CoreDataでsqliteを利用したアプリ開発をしていると、シミュレーターで登録したデータの確認をするなどの目的で、sqliteのファイルを直接見たいことがある。

但し、sqliteのファイルパスが直感的ではないため探すのが面倒臭い。


ファイルパス

sqliteは、以下のディレクトリ構成で格納されるようだ。


sqliteのファイルパス

~/Library/Developer/CoreSimulator/Devices/[デバイス固有文字列]/data/Containers/Data/Application/[アプリケーション固有文字列]/Documents/[アプリケーション名].sqlite


上記パスの下記文字列がランダムな文字列になっている。


  • デバイス固有文字列

  • アプリケーション固有文字列

このうちデバイス固有文字列は、以下のファイルの中の「dict -> dict -> string」に定義されている。


DeviceID一覧が記載されているファイル

~/Library/Developer/CoreSimulator/Devices/device_set.plist


このファイルの中身は、以下のようになっている。


device_set.plist

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DefaultDevices</key>
<dict>
<key>com.apple.CoreSimulator.SimRuntime.iOS-8-0</key>
<dict>
<key>com.apple.CoreSimulator.SimDeviceType.Resizable-iPad</key>
<string>英数字のランダム文字列がハイフン区切りで格納されている</string>
<key>com.apple.CoreSimulator.SimDeviceType.Resizable-iPhone</key>
<string>英数字のランダム文字列がハイフン区切りで格納されている</string>
:
<省略>


簡単にパスを取得する方法

ターミナルで以下のコマンドを実行すれば、sqliteのファイル一覧を日付更新順(降順)に出力できる。

% ls -t ~/Library/Developer/CoreSimulator/Devices/*/data/Containers/Data/Application/*/Documents/*.sqlite

sqliteのファイル名にアプリケーションの名称が入るため、開発しているアプリの数が少なければ、このコマンドを実行するだけで充分かもしれない。

ただし、同じアプリでも、複数のiOSのバージョンや複数のデバイスで動作確認をすると思うので、デバイス固有文字列と、


  • iOSバージョン(iOS-9-2, iOS-9-3,...)

  • モデル(iPhone-6s、iPad-Air,...)

の対応付けをしておくと便利だと思う。

そこで、device_set.plistを解析して、これらの一覧を表示できるスクリプトを作ってみた。


getXcodeAppliPath.rb

#! /usr/bin/env ruby

require 'rexml/document'

# File path : device_set.plist
DEVICE_SET_PLIST="#{Dir.home}/Library/Developer/CoreSimulator/Devices/device_set.plist"
SQLITE_LIST = "#{Dir.home}/Library/Developer/CoreSimulator/Devices/*/data/Containers/Data/Application/*/Documents/*.sqlite"

# Device information
class Device
def initialize(os, model)
@os = os
@model = model
end

attr_reader :os, :model

def to_s()
sprintf("%s %s", os , model)
end
end

# This class hold all device information of defined by device_set.plist
class DeviceHolder
def initialize()
@device = {}
end

def push(id, os, model)
@device[id] = Device.new(os, model)
end

def get(id)
@device[id]
end

def ids
@device.key
end

def to_s(id)
get(id).to_s
end
end

class DeviceSetPlistReader
def initialize(filename)
@filename = filename
@holder = DeviceHolder.new
end

def execute
doc = REXML::Document.new(open(@filename))

default_device = doc.root.elements['dict/dict']

os_name = nil
default_device.elements.each do |el|
if 'key' == el.name
os_name = trimming(el.text)
elsif 'dict' == el.name
read_device_id(el, os_name)
end
end

@holder
end

private

def trimming(text)
text.split('.').last
end

def read_device_id(target, os_name)
# Ex.
# <key>com.apple.CoreSimulator.SimDeviceType.Resizable-iPad</key>
# <string>DEVICE_ID1</string>
# <key>com.apple.CoreSimulator.SimDeviceType.Resizable-iPhone</key>
# <string>DEVICE_ID2</string>
model = nil
target.elements.each do |el|
if 'key' == el.name
model = trimming(el.text)
elsif 'string' == el.name
@holder.push(el.text, os_name, model)
end
end
end
end

#### Start process

reader = DeviceSetPlistReader.new(DEVICE_SET_PLIST)
holder = reader.execute

Dir.glob(SQLITE_LIST).sort{|x,y| -1*(File::stat(x) <=> File::stat(y))}.each do |file|
dirs = file.split('/')
device_id = dirs[-8] # Device ID is in the 8th from behind.
application_name = dirs[-1].sub(/.sqlite$/, '')
printf("%s\n %s\n %s\n", application_name, holder.to_s(device_id), file)
end


実行すると以下のようになる。

% ruby getXcodeAppliPath.rb

DummyAppli
iOS-9-3 iPhone-6s
/Users/hoge/Library/Developer/CoreSimulator/Devices/XXXX-XXXX-XXXX/data/Containers/Data/Application/XXXX-XXXX-XXXX/Documents/DummyAppli.sqlite
DummyAppli
iOS-9-2 iPhone-6s
/Users/hoge/Library/Developer/CoreSimulator/Devices/XXXX-XXXX-XXXX/data/Containers/Data/Application/XXXX-XXXX-XXXX/Documents/DummyAppli.sqlite


さいごに

ググると、アプリケーションのパスを表示してくれるソフトは、有料だといくつかあるらしい(ex. SimPholdersとか)。

こういうアプリだとメニューバーに常駐させてグラフィカルに操作できたりするので便利そうだ。

Scala版も作成してみました。