ファイルを保存するだけで動的にプログラムを更新できるInjectionIIIについて、macOSでもできるようにしてみた
以前、ファイルを保存するだけで動的にプログラムを更新できるInjectionIIIについての解説記事を投稿した。
その際は、対象がiOSだけだった。macOSでもできると書いてあったが、方法がわからず、ようやく少し進展した(まだ道半ば)。
https://qiita.com/KoichiroEto/items/5cb149a6e5d74bbdd66c
3. macOSのプログラムからインジェクションしてみる
これまでは、iOS用アプリをインジェクションしていた。iOSアプリはシミュレーター上で動作しており、macOSアプリはそうではないという違いがある。そのため、いくつか追加の手順が必要となる。
3.1. なにかアプリを作る
まず、さきほどと同様に、なにかシンプルなアプリを開発する。
Xcodeを起動→Create a new Xcode Project→macOS→「App」→Next→Product Name:「MacTest」、User Interface: Storyboard→Next→「~/dev」を指定→Create
ViewController.swiftに、以下のようにshow()を追加。viewDidLoad()から呼ばれるようにする。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
show()
}
func show() {
let label = NSTextField(frame: NSRect(x: Int.random(in: 10..<100), y: Int.random(in: 10..<100), width: 150, height: 50))
label.backgroundColor = NSColor.cyan
label.stringValue = "Hello, world!"
view.addSubview(label)
}
}
(TextFieldの位置がランダムなのは、諸事情がある。後述する。)
まずはこの段階で実行してみる。Cmd-R→ビルドされ、実行される。ウィンドウが表示され、「Hello, world!」が表示される。
ViewController.swiftに戻り、"Hello, world!"
を"Hello, Japan!"
に修正してCmd-Sで保存する。当然、何も反映されない。この時点ではまだインジェクションされていないからだ。
再度Cmd-Rすると、一旦アプリが終了し、再度立ち上げられ、"Hello, Japan!"
が表示される。約3秒で立ち上がる。この速度ならあまり不満は持たれないかもしれない。
該当個所を、「Hello, world!」に戻しておく。インジェクションの設定を始めてみよう。
3.2. プロジェクトを設定する
Xcodeに戻る。
Cmd-1→MacTestのプロジェクトを選択→PROJECT: MacTest→Build Settings→Linking→Other Linker Flags→ここにカーソルを乗せると左に三角が表示されるので、それをクリックする→Debugの右の「+」をおす→Any Architecture | Any SDK:「-Xlinker -interposable」→リターンを押すと確定する
Cmd-1→MacTestのプロジェクトを選択→TARGETS: MacTest→Signing & Capabilities→All→App Sandoboxの右の小さな「×」を押して、消す
Cmd-1→MacTestのプロジェクトを選択→TARGETS: MacTest→Signing & Capabilities→All→Hardened Runtime→「Disable Library Validation」をcheck
3.3. Bundleを追加
AppDelegate.swiftにBundleを追加する。
#if DEBUG
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()
#endif
参考までに、AppDelegate.swiftの該当するメソッド全体を示す。
func applicationDidFinishLaunching(_ aNotification: Notification) {
#if DEBUG
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()
#endif
}
3.4. injected()を追加
ViewController.swift
に、injectedというメソッドを追加する。
@objc func injected() {
show()
}
参考までに、ViewController classの全体である。
import Cocoa
class ViewController: NSViewController {
@objc func injected() {
show()
}
override func viewDidLoad() {
super.viewDidLoad()
show()
NotificationCenter.default.addObserver(self, selector: Selector("injected"), name: NSNotification.Name(rawValue: "INJECTION_BUNDLE_NOTIFICATION"), object: nil)
}
func show() {
let label = NSTextField(frame: NSRect(x: Int.random(in: 10..<100), y: Int.random(in: 10..<100), width: 150, height: 50))
label.stringValue = "Hello, world!"
label.backgroundColor = NSColor.cyan
view.addSubview(label)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
3.5. InjectionIIIにProjectを指定する
InjectionIIIが起動されていなかったら、起動する。
Status menuのInjectionIIIから「Open project」を選択→「~/dev/MacTest」を選択→「Select Project Directory」
3.6. 起動する
Cmd-R→アプリが起動して、「Hello, world!」が表示される。
この状態で、Hello, world!を編集してみる。Cmd-Sで保存する。そうすると、即座にコンパイルされ、読み込まれ、classがreplaceされる。
また、injectedが呼ばれ、そこからshowが呼ばれる。
ただ、前のオブジェクトが残ってしまっている。そのため、以前のTextFieldは消去されない。そのまま残るだけである。
そのため、以前のversionでは場所が固定されているので、文字が変更されない。これが更新される方法は、これから調べる予定。
とりあえず、今日はここまで!
Author And Source
この問題について(ファイルを保存するだけで動的にプログラムを更新できるInjectionIIIについて、macOSでもできるようにしてみた), 我々は、より多くの情報をここで見つけました https://qiita.com/KoichiroEto/items/80d4f6429a331e6e8e15著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .