使わなくなったiPhoneで自宅の回線速度を記録してみた

:::note info
この記事は[ZOZO Advent Calendar 2022](https://qiita.com/advent-calendar/2022/zozo)Vol.1の22日目の記事です。
:::

# はじめに
こんにちは。株式会社ZOZOでiOSエンジニアをしている[@_DS_Store](https://twitter.com/_DS_Store)です。Qiitaに投稿するのは3年ぶりです。
今回はiPhoneを使って家の回線速度を記録しスプレッドシートに書き込むアプリを開発してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256520/43fb7881-e053-8147-1f14-f11fd4e3bf0d.png)


# きっかけ
今アパートに住んでいて備え付けの回線を使っているのですが、めちゃめちゃ回線が遅くなる時があり快適に仕事やゲームができないことがあります。そこで回線速度を記録して今後の生活に役立てようと思った次第です。

# 実装
## 回線速度の測定
回線速度を測るために[speedchecker-sdk-ios](https://github.com/speedchecker/speedchecker-sdk-ios)というSDKを使用します。以下のようにXCFrameworkが提供されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256520/f85bc0a4-cbc5-fc8c-05a4-f8cde5cc1917.png)

直接ダウンロードしてプロジェクトに取り込んでもよかったのですがどうせならSPMに対応させたいと思ったので、[フォーク](https://github.com/NakaokaRei/speedchecker-sdk-ios)してきて`Package.swift`を追加しました。PRも出しているのでマージされてほしい。

https://github.com/speedchecker/speedchecker-sdk-ios/pull/19

## 使用方法
SDKをimportして`InternetSpeedTest`クラスの`startTest()`を呼び出すと回線速度の測定が開始します。これをTimerで定期的に実行します。

```SpeedTestManager.swift
import SpeedcheckerSDK
import CoreLocation

protocol SpeedTestDelegate {
    func progressDonwload(downloadMbps: Double)
    func progressUpload(uploadMbps: Double)
}

class SpeedTestManager: NSObject, CLLocationManagerDelegate {

    private var internetTest: InternetSpeedTest?
    private var locationManager = CLLocationManager()

    private var completion: ((_ download: Double, _ upload: Double) -> Void)?
    var delegate: SpeedTestDelegate? = nil

    override init() {
        super.init()
        Task {
            if CLLocationManager.locationServicesEnabled() {
                locationManager.delegate = self
                locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
                locationManager.requestWhenInUseAuthorization()
                locationManager.requestAlwaysAuthorization()
                locationManager.startUpdatingHeading()
            }
        }
    }

    // 計測開始、測定が終了したときに呼び出すcompletionを受け取る。
    func startTest(completion: @escaping (_ download: Double, _ upload: Double) -> Void) {
        internetTest = InternetSpeedTest(delegate: self)
        internetTest?.startTest() { error in
            if error != .ok {
                print(error)
            }
        }
        self.completion = completion
    }
}

extension SpeedTestManager: InternetSpeedTestDelegate {
    func internetTestFinish(result: SpeedTestResult) {
        if let completion {
            completion(result.downloadSpeed.mbps, result.uploadSpeed.mbps)
        }
    }

    func internetTestDownload(progress: Double, speed: SpeedTestSpeed) {
        delegate?.progressDonwload(downloadMbps: speed.mbps)
    }

    func internetTestUpload(progress: Double, speed: SpeedTestSpeed) {
        delegate?.progressUpload(uploadMbps: speed.mbps)
    }
}

```

```SpeedTestViewConfig.swift
@MainActor
class SpeedTestViewConfig: ObservableObject {
    
    // Viewに表示する測定結果
    @Published var downloadMbps: Double = 0
    @Published var uploadMbps: Double = 0
    @Published var latestDate: Date = Date()

    let speedTestManager = SpeedTestManager()
    var timer: Timer?

    // タイマーで定期的に実行
    init() {
        self.timer = Timer.scheduledTimer(
            timeInterval: 60 * 5,
            target: self,
            selector: #selector(self.startTest),
            userInfo: nil,
            repeats: true
        )
        speedTestManager.delegate = self
    }

    // 計測開始
    @objc func startTest() {
        downloadMbps = 0
        uploadMbps = 0
        speedTestManager.startTest { downloadMbps, uploadMbps in
            Task.detached { @MainActor in
                self.latestDate = Date()
                self.downloadMbps = downloadMbps
                self.uploadMbps = uploadMbps
                try await self.post(
                    time: self.formattedDate(date: self.latestDate),
                    uploadSpeed: String(uploadMbps),
                    downloadSpeed: String(downloadMbps)
                )
            }
        }
    }

    private func post(time: String, uploadSpeed: String, downloadSpeed: String) async throws {
    // 結果をサーバに送信
    }
}

extension SpeedTestViewConfig: SpeedTestDelegate {
    func progressDonwload(downloadMbps: Double) {
        self.downloadMbps = downloadMbps
    }

    func progressUpload(uploadMbps: Double) {
        self.uploadMbps = uploadMbps
    }
}

```


## 結果の記録
測定結果はHTTPリクエストでGASで書かれたWebサーバに送られ、スプレッドシートに記録されます。

```js
function doPost(e) {
  var jsonString = e.postData.getDataAsString();
  var data = JSON.parse(jsonString);

  var time = data.time;
  var uploadSpeed = data.uploadSpeed;
  var downloadSpeed = data.downloadSpeed;

  var ss = SpreadsheetApp.openById(SpreadsheetApp.getActiveSpreadsheet().getId());
  var sheet = ss.getSheetByName("シート1");

  sheet.appendRow([time, uploadSpeed, downloadSpeed])
}

```

# おわりに
今回はiPhoneを使って家の回線速度を記録するアプリを開発しました。数日間記録してみてやっぱり夕方から夜の時間帯は遅いです。
全体の実装は以下のリポジトリにあるので気になる方はこちらから見てみてください。

https://github.com/NakaokaRei/SpeedTestLogger-iOS