サーバーサイドエンジニアとやり取りする時など、何かとcurl
コマンドが欲しい機会があると思います。
Charlesなどの外部ツールを使っても出来ますが、LLDBで扱えると時短になります。
Swift側のextension
HEAD
リクエストなどを考慮していないため、厳密ではない部分はありますが、実務で使うようなリクエストにはシンプルなコードで対応できます。
必要に応じて、オプションをcomponents
に追加することで調整可能です。
extension URLRequest { var curlCommand: String? { guard let method = httpMethod, let url = url?.absoluteString else { return nil } var components = ["curl", "-X", method, "'\(url)'"] for (key, value) in allHTTPHeaderFields ?? [:] { components.append("-H") components.append("'\(key): \(value)'") } if let httpBody, let body = String(data: httpBody, encoding: .utf8) { components.append("-d") components.append("'\(body)'") } return components.joined(separator: " ") } }
let request = URLRequest(url: .init(string: "https://www.hogehoge.com")!) print(request.curlCommand!) // curl -X GET 'https://www.hogehoge.com'
LLDB用のPythonスクリプト
プロダクトに入れる必要は全くないので、swiftファイルをプロジェクト外に置いておき、LLDBのスクリプトで読ませるのがおすすめです。
今回はSwift側で整形するので、Python側の処理はswiftファイルを読み込んで、そのままLLDBに食わせるだけです。
import lldb import lldbcommon as common import os @lldb.command("curl") def print_curl(debugger, exp, result, dict): path = os.path.join(os.path.dirname(__file__), 'swift/URLRequest+curlCommand.swift') with open(path, 'r') as f: common.evaluate(f.read()) common.evaluate(f'print({exp}.curlCommand!)')
毎回swiftファイルをロードしてしまうので、 気になる方はPython側で制御を追加してください。
common.evaluate
の実装の一例は以下の通りです。
def evaluate(exp): options = lldb.SBExpressionOptions() options.SetLanguage(lldb.eLanguageTypeSwift) options.SetTimeoutInMicroSeconds(3 * 1000 * 1000) options.SetTrapExceptions(False) frame = ( lldb.debugger.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) value = frame.EvaluateExpression(exp, options) error = value.GetError() if not error.Success() and error.value != 0x1001: print(error) return value