背景
うちの賃貸マンションはNuro光が無料で使えるのですが、手持ちのMacと相性が悪いのか、突然接続が切れることが度々あります。1日に1回以上の頻度で発生するため、さすがに対策が必要でした。
単にこちらをShell scriptあるいはApple Scriptで実装してもよいのですが、稼働している様子がわかるようにしたく、メニューバーに常駐するタイプのアプリケーションとして実装することにしました。
方法
基本的には、こちらのチュートリアルの通りに進めてメニューバー常駐アプリを作りました。
Swiftからshellコマンドを実行する方法としてこちらを使いました。
完成コード
AppDelegate.swift
import Cocoa
protocol CommandExecuting {
func run(commandName: String, arguments: [String]) throws -> String
}
enum BashError: Error {
case commandNotFound(name: String)
}
struct Bash: CommandExecuting {
func run(commandName: String, arguments: [String] = []) throws -> String {
return try run(resolve(commandName), with: arguments)
}
private func resolve(_ command: String) throws -> String {
guard var bashCommand = try? run("/bin/bash" , with: ["-l", "-c", "which \(command)"]) else {
throw BashError.commandNotFound(name: command)
}
bashCommand = bashCommand.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
return bashCommand
}
private func run(_ command: String, with arguments: [String] = []) throws -> String {
let process = Process()
process.launchPath = command
process.arguments = arguments
let outputPipe = Pipe()
process.standardOutput = outputPipe
process.launch()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
return output
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var menu: NSMenu!
//メニューバーに表示されるアプリケーションを作成
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.variableLength)
func applicationDidFinishLaunching(_ aNotification: Notification) {
// メニューバーに表示されるアプリ。今回は文字列で設定
statusItem.title = "NURO"
statusItem.highlightMode = true
statusItem.menu = menu
//アプリ終了ボタンを作成
let quitItem = NSMenuItem()
quitItem.title = "Quit Application"
quitItem.action = #selector(AppDelegate.quit(_:))
menu.addItem(quitItem)
// WiFi再接続のループ開始
startLoop()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
@objc func quit(_ sender: Any) {
NSApplication.shared.terminate(self)
}
func startLoop() {
Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block:{ (timer) in
let bash: CommandExecuting = Bash()
var result = ""
do {
try result = bash.run(commandName: "networksetup", arguments: ["-getinfo", "Wi-Fi"])
}catch {
print("failed to networksetup -getinfo")
return
}
if (!result.contains("Subnet mask")) {
do {
try result = bash.run(commandName: "networksetup", arguments:
["-setairportnetwork", "en1", "HG8045-D94E-bg", "cuayh5ks"]
)
} catch {
print("failed to networksetup -setairportnetwork")
}
}
})
}
}
ポイント
.entitlementsファイルで、App Sandbox
をNO
にする必要がある。