{"id":19966755,"url":"https://github.com/aptpod/intdash-swift","last_synced_at":"2026-06-10T00:31:15.249Z","repository":{"id":56915561,"uuid":"354752238","full_name":"aptpod/intdash-swift","owner":"aptpod","description":"intdash SDK for Swift","archived":false,"fork":false,"pushed_at":"2021-12-08T11:46:42.000Z","size":13163,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-13T16:48:34.917Z","etag":null,"topics":["cocoapods","sdk","swift"],"latest_commit_sha":null,"homepage":"https://docs.intdash.jp/sdk/swift/latest/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aptpod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-05T07:19:21.000Z","updated_at":"2021-12-08T11:41:36.000Z","dependencies_parsed_at":"2022-08-20T20:50:20.158Z","dependency_job_id":null,"html_url":"https://github.com/aptpod/intdash-swift","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fintdash-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fintdash-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fintdash-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aptpod%2Fintdash-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aptpod","download_url":"https://codeload.github.com/aptpod/intdash-swift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241398920,"owners_count":19956826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cocoapods","sdk","swift"],"created_at":"2024-11-13T02:38:06.878Z","updated_at":"2026-06-10T00:31:15.239Z","avatar_url":"https://github.com/aptpod.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# intdash SDK for Swift\n\n[ドキュメントホーム](https://docs.intdash.jp/sdk/swift/latest/)\n\n## ■ このSDKを使用するために必要な開発環境\n\n- Xcode 11.3以上\n\n## ■ 動作環境\n\n- iOS 12以上\n\n## ■ 事前準備\n\n1\\. intdashサーバーの管理者からOAuth2.0のクライアントIDを入手します。  \n（例：`abcdefg123456`）  \n2\\. これから開発するアプリケーションのコールバックスキーム名を決め、intdashサーバーの管理者へスキーム名の登録を依頼してください。  \n（例： `companyname.appname`）  \n3\\. `Info.plist` の `URL Types`で、以下のようにコールバックスキームを登録します。  \n例)\n\t\n    |Key                |Type       |Value                             |\n    |-------------------|-----------|----------------------------------|\n    |- URL types        |Array      |                                  |\n    | - Item 0 (Viewer) |Dictionary |                                  |\n    |  - Document Role  |String     |Viewer                            |\n    |  - URL identifier |String     |$(PRODUCT_BUNDLE_IDENTIFIER)      |\n    |  - URL Schemes    |Array      |                                  |\n    |   - Item 0        |String     |スキーム名（例：companyname.appname）   |\n\n4\\. `Intdash.xcframework` をプロジェクトに追加します。  \n\n※ 手動で開発するプロジェクトへフレームワークを追加した場合は `Embed \u0026 Sign` としてください。\n\n### Cocoapods\nこのフレームワークは [CocoaPods](http://cocoapods.org) からも入手可能です、`Podfile` に以下を追加してください。\n\n```ruby\npod 'Intdash'\n```\n\n## ■ 実装\n\n### ・認証\n\n1\\. フレームワークをインポートする。  \n2\\. `IntdashClient.Session` を初期化する。  \n3\\. `IntdashClient.OAuth2API` を初期化する。  \n4\\. Web認証用URLを生成する。  \n5\\. 認証を開始する。  \n6\\. 認証結果が正しいかチェックする。  \n7\\. 正常に認証ができていれば認証コードを利用してアクセストークンを取得する。  \n8\\. 必要に応じて、サインインしたエッジ（自分自身）の情報を取得する。  \n\n```swift\n// 1. フレームワークをインポートする。\nimport Intdash\nimport AuthenticationServices\n\n// intdashサーバー名\nlet kTargetServer: String = \"https://example.com\"\n// OAuth2.0 クライアントID\nlet kIntdashClientId: String = \"abcdefg123456\"\n// コールバックスキームを使用したコールバックURL\nlet kCallbackURLScheme: String = \"companyname.appname://oauth2/callback\"\n\nclass ExampleViewController: UIViewController {\n\n    var session: IntdashClient.Session?\n    var signInEdgeUuid: String?\n    var signInEdgeName: String?\n    \n    private var webAuthSession: NSObject?\n\n    func signIn() {\n        guard #available(iOS 12.0, *) else {\n            print(\"Unsupported OS\")\n            return\n        }\n        // 2. `IntdashClient.Session` を初期化する。\n        let session = IntdashClient.Session(serverURL: kTargetServer, clientId: kIntdashClientId)\n        self.session = session\n        // 3. `IntdashClient.OAuth2API` を初期化する。\n        let oauth2Api = IntdashClient.OAuth2API(session: session)\n        // 4. Web認証用URLを生成する。\n        let callbackURLScheme = kCallbackURLScheme.replacingOccurrences(of: \":\", with: \"%3A\").replacingOccurrences(of: \"/\", with: \"%2F\") // URLエンコード\n        oauth2Api.generateAuthorizationURL(callbackURLScheme: callbackURLScheme) { [weak self] (url, codeVerifier, state, error) in\n            guard error == nil, let url = url, let authURL = URL(string: url), let codeVerifier = codeVerifier else {\n                print(\"generateAuthorizationURL failed. \\(error?.localizedDescription ?? \"\")\")\n                return\n            }\n            // 5. 認証を開始する。\n            let webAuthSession = ASWebAuthenticationSession(url: authURL, callbackURLScheme: callbackURLScheme) { (callbackURL, error) in\n                guard error == nil, let callbackURL = callbackURL else {\n                    print(\"Web authentication callback error. \\(error?.localizedDescription ?? \"\")\")\n                    return\n                }\n                // 6. 認証結果が正しいかチェックする。\n                var result = false\n                var code: String?\n                if let queryItems = URLComponents(string: callbackURL.absoluteString)?.queryItems {\n                    for item in queryItems {\n                        if item.name == \"state\", item.value == state {\n                            result = true\n                        }\n                        if item.name == \"code\" {\n                            code = item.value\n                        }\n                    }\n                }\n                if let code = code, result {\n                    print(\"Web authentication was successful.\")\n                    // 7. 正常に認証ができていれば認証コードを利用してアクセストークンを取得する。\n                    // (※このとき `IntdashClient.Session` の認証情報はフレームワーク側で自動で更新されます。)\n                    oauth2Api.authenticate(code: code, codeVerifier: codeVerifier, callbackURLScheme: kCallbackURLScheme) { (response, error) in\n                        if let error = error {\n                            print(\"requestAccessToken failed. \\(error.localizedDescription)\")\n                            return\n                        }\n                        // 8. 必要に応じて、サインインしたエッジ（自分自身）の情報を取得する。\n                        // (※このあとデータのアップストリームを行う場合はエッジのUUIDが必要です。)\n                        let edgesApi = IntdashClient.EdgesAPI(session: session)\n                        edgesApi.me { (response, error) in\n                            guard let response = response else {\n                                print(\"requestEdgesMe failed. \\(error?.localizedDescription ?? \"\")\")\n                                return\n                            }\n                            print(\"Successful sign-in.\")\n                            self?.signInEdgeName = response.name\n                            self?.signInEdgeUuid = response.uuid\n                        }\n                    }\n                }\n            }\n            if #available(iOS 13.0, *) {\n                webAuthSession.presentationContextProvider = self\n                webAuthSession.prefersEphemeralWebBrowserSession = false\n            }\n            \n            // Start\n            self?.webAuthSession = webAuthSession\n            webAuthSession.start()\n        }\n    }\n}\n\nextension ExampleViewController: ASWebAuthenticationPresentationContextProviding {\n    \n    @available(iOS 12.0, *)\n    func presentationAnchor(for session: ASWebAuthenticationSession) -\u003e ASPresentationAnchor {\n        return UIApplication.shared.windows.first ?? ASPresentationAnchor()\n    }\n    \n}\n\n\n```\n\n### ・エッジ、計測、キャプチャーなどintdash APIが提供するリソースの取得\n\n例として、ここではエッジの一覧を取得します。\n\n1\\. `IntdashClient` を初期化する。  \n2\\. 認証情報がセットされた `IntdashClient.Session` をセットする。  \n3\\. エッジの一覧を取得する。  \n\n```swift\n// 1. IntdashClientを初期化する。\nlet intdash = IntdashClient()\n\n// 2. 認証情報がセットされた `IntdashClient.Session` をセットする。\nintdash.session = self.session\n\n// 3. エッジの一覧を取得する。\nintdash.edges.list { (response, error) in\n    guard let response = response else {\n        print(\"requestEdgeList failed. \\(error?.localizedDescription ?? \"\")\")\n        return\n    }\n    print(\"successful request for edge list. \\(response.items.count) edges\")\n    for item in response.items {\n        print(\"\\(item.name) [\\(item.uuid) \")\n    }\n}\n```\n\n### ・リアルタイムデータのダウンストリームを行う\n\n1\\. `IntdashClient` を初期化する。  \n2\\. 認証情報がセットされた `IntdashClient.Session` をセットする。  \n3\\. `IntdashClient.DownstreamManager` のデリゲートを設定する。  \n4\\. intdashサーバーとの接続を開始する。  \n5\\. ダウンストリームを開く。  \n6\\. ダウンストリームフィルター(`IntdashClient.DownstreamManager.RequestFilters`)を生成する。  \n7\\. ダウンストリーム情報をintdashサーバーと同期する。  \n\n```swift\nvar intdash: IntdashClient?\nvar downstreamIds: [Int]?\n\n/// ダウンストリームを行う対象のエッジのUUID\nvar downstreamTargetEdgeUuid = \"\" // 必要に応じて変更\n/// ダウンストリームを行う対象のエッジのチャンネル番号\nvar targetChannel: Int = 1 // 必要に応じて変更\n\nfunc startDownsteram() {\n    // 1. `IntdashClient` を初期化する。\n    let intdash = IntdashClient()\n    // 2. 認証情報がセットされた `IntdashClient.Session` をセットする。\n    intdash.session = self.session\n    self.intdash = intdash\n    // 3. `IntdashClient.DownstreamManager` のデリゲートを設定する。\n    intdash.downstreamManager.addDelegate(delegate: self) // IntdashClientDownstreamManagerDelegate\n    // 4. intdashサーバーとの接続を開始する。\n    intdash.connect { [weak self] (error) in\n        if let error = error {\n            print(\"Failed to connect to the intdash server. \\(error.localizedDescription)\")\n            return\n        }\n        // 5. ダウンストリームを開く。\n        let streamId: Int\n        do {\n            streamId = try intdash.downstreamManager.open(srcEdgeId: self!.downstreamTargetEdgeUuid)\n        } catch {\n            print(\"Failed to open downstream. \\(error)\")\n            return\n        }\n        if self?.downstreamIds == nil {\n            self?.downstreamIds = [streamId]\n        } else {\n            self?.downstreamIds?.append(streamId)\n        }\n        // 6. ダウンストリームフィルター(`IntdashClient.DownstreamManager.RequestFilters`)を生成する。\n        let downstreamFilter = self?.makeDownstreamFilters(streamId: streamId, channel: self!.targetChannel)\n        // 7. ダウンストリーム情報をintdashサーバーと同期する。(※downstreamFiltersにnilを指定すると、全チャンネル、全データタイプ、全IDのデータを受信します)\n        intdash.downstreamManager.sync(completion: { (errors) in\n            if let errors = errors {\n                print(\"Failed to request dowsntream. \\(errors)\")\n                return\n            }\n            print(\"Success to downstream request.\")\n        }, filters: downstreamFilter)\n    }\n}\n```\n\n#### ダウンストリームフィルターの生成方法\n\n8\\. `IntdashClient.DownstreamManager.RequestFilters` を初期化する。  \n9\\. ダウンストリームするデータの情報を追加する。  \n\n```swift\nfunc makeDownstreamFilters(streamId: Int, channel: Int) -\u003e IntdashClient.DownstreamManager.RequestFilters? {\n    // 8. `IntdashClient.DownstreamManager.RequestFilters` を初期化する。\n    let downstreamFilters = IntdashClient.DownstreamManager.RequestFilters()\n\n    // 9. ダウンストリームするデータの情報を追加する。\n    // フィルターが必要ない(全チャンネル、データを対象とする)場合は何もappendしない。\n    // 「チャンネル1、データタイプGeneralSensor、ID 1,3,4」を追加する場合\n    downstreamFilters.append(streamId: streamId, channelNum: 1, dataType: .generalSensor, ids: [1, 3, 4])\n    // 「チャンネル1、データタイプH.264、IDなし」を追加する場合\n    downstreamFilters.append(streamId: streamId, channelNum: channel, dataType: .h264, id: nil)\n\n    // フィルターが追加されなかった場合は全開放ファイルターとして扱われます。\n    return downstreamFilters\n}  \n```\n\n#### ダウンストリームされたデータの取得方法\n\n10\\. `IntdashClientDownstreamManagerDelegate.downstreamManagerDidParseDataPoints` から `RealtimeDataPoint` の配列を取得する。  \n\n```swift\nextension ExampleViewController: IntdashClientDownstreamManagerDelegate {\n    \n    // 10. `IntdashClientDownstreamManagerDelegate.downstreamManagerDidParseDataPoints` から `RealtimeDataPoint` の配列を取得する。\n    func downstreamManagerDidParseDataPoints(_ manager: IntdashClient.DownstreamManager, streamId: Int, dataPoints: [RealtimeDataPoint]) {\n        for dataPoint in dataPoints {\n            switch dataPoint.dataModel.dataType {\n                // ...\n            default: break\n            }\n        }\n    }\n        \n}\n```\n\n#### ダウンストリームを終了する\n\n11\\. ダウンストリームを閉じる。  \n12\\. 閉じられているダウンストリームを削除する。  \n13\\. intdashサーバーとの接続を終了する。(※終了する場合)  \n\n```swift\nfunc stopDownstream() {\n    guard let intdash = self.intdash else { return }\n    self.intdash = nil\n    let group = DispatchGroup()\n    if let streamIds = self.downstreamIds {\n        self.downstreamIds = nil\n        group.enter()\n        DispatchQueue.global().async {\n            // 11. ダウンストリームを閉じる。\n            intdash.downstreamManager.close(streamIds: streamIds) { (error) in\n                if let error = error {\n                    print(\"Failed to close downstream. \\(error.localizedDescription)\")\n                } else {\n                    print(\"Success to close downstream.\")\n                }\n                // 12. 閉じられたダウンストリームを削除する。\n                intdash.downstreamManager.removeClosedDownstream()\n                group.leave()\n            }\n        }\n    }\n    \n    group.notify(queue: .global()) {\n        // 13. intdashサーバーとの接続を終了する。(※終了する場合)\n        intdash.disconnect { (error) in\n            if let error = error {\n                print(\"Failed to disconnect to the intdash server. \\(error.localizedDescription)\")\n            } else {\n                print(\"Success to disconnect to the intdash server.\")\n            }\n        }\n    }\n}\n```\n\n\n### ・リアルタイムデータのアップストリームを行う(新しい計測を開始し、サーバーにリアルタイムデータを送信する)\n\n1\\. `IntdashClient` を初期化する。  \n2\\. 認証情報がセットされた `IntdashClient.Session` をセットする。  \n3\\. `IntdashClient.UpstreamManager` のデリゲートを設定する。(※セクション情報の管理やAckの確認を行いたい場合のみ)  \n4\\. intdashサーバーとの接続を開始する。  \n5\\. 計測IDを取得する。  \n6\\. アップストリームを開く。  \n7\\. アップストリーム情報をintdashサーバーと同期する。  \n8\\. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager` を初期化する。  \n9\\. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager.setMeasurementId()` で計測IDを保存する。  \n\n```swift\n//var intdash: IntdashClient?\nvar upstreamId: Int?\nvar upstreamIds: [Int]?\n\n/// アップストリームを行うチャンネル番号(※ストリームごとに別のチャンネル番号を設定することができます)\n//var targetChannel: Int = 1 // 必要に応じて変更\n\n/// iOSデバイス内にデータを保存する場合に使用するファイルマネージャー\nvar intdashDataFileManager: IntdashDataFileManager?\n/// intdashサーバーへ保存するかの選択\nvar isSaveToServer: Bool = true\n/// intdashデータを保存するディレクトリのパス\nvar intdashDataFileParentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]\n\nfunc startUpstream() {\n    guard let edgeUuid = self.signInEdgeUuid else { return }\n    // 1. `IntdashClient` を初期化する。\n    let intdash = IntdashClient()\n    self.intdash = intdash\n    // 2. 認証情報がセットされた `IntdashClient.Session` をセットする。\n    intdash.session = self.session\n    // 3. IntdashClient.UpstreamManagerのデリゲートを設定する。(※セクション情報の管理やAckの確認を行いたい場合のみ)\n    intdash.upstreamManager.addDelegate(delegate: self) // IntdashClientUpstreamManagerDelegate\n    // 4. intdashサーバーとの接続を開始する。\n    intdash.connect { [weak self] (error) in\n        if let error = error {\n            print(\"Failed to connect to the intdash server. \\(error.localizedDescription)\")\n            return\n        }\n        // 5. 計測IDを取得する。\n        intdash.upstreamManager.requestMeasurementId(edgeUuid: edgeUuid) { [weak self] (measurementId, error) in\n            guard let measurementId = measurementId else {\n                print(\"Failed to requestMeasurementId. \\(error?.localizedDescription ?? \"\")\")\n                return\n            }\n            // 6. アップストリームを開く。\n            let streamId: Int\n            do {\n                // データをサーバーに保存する場合は `store` を `true` にしてください。\n                streamId = try intdash.upstreamManager.open(measurementId: measurementId, srcEdgeId: edgeUuid, store: self!.isSaveToServer)\n            } catch {\n                print(\"Failed to open upstream. \\(error)\")\n                return\n            }\n            self?.upstreamId = streamId\n            if self?.upstreamIds == nil {\n                self?.upstreamIds = [streamId]\n            } else {\n                self?.upstreamIds?.append(streamId)\n            }\n            // 7. アップストリーム情報をintdashサーバーと同期する。\n            intdash.upstreamManager.sync { [weak self] (error) in\n                if let error = error {\n                    print(\"Failed to request upstream. \\(error)\")\n                    return\n                }\n                print(\"Success to request upstream.\")\n                if self!.isSaveToServer {\n                    do {\n                        // 8. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager` を初期化する。\n                        let fileManager = try IntdashDataFileManager(parentPath: \"\\(self!.intdashDataFileParentPath)/\\(measurementId)\")\n                        // 9. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager.setMeasurementId()` で計測IDを保存する。\n                        try fileManager.setMeasurementId(id: measurementId)\n                        self?.intdashDataFileManager = fileManager\n                    } catch {\n                        print(\"Failed to setup file manager. \\(error.localizedDescription)\")\n                    }\n                }\n            }\n        }\n    }\n}\n\nextension ExampleViewController: IntdashClientUpstreamManagerDelegate {\n    \n    func upstreamManager(_ manager: IntdashClient.UpstreamManager, didGeneratedSesion sectionId: Int, sectionIndex: Int, streamId: Int, final: Bool, sentCount: Int, startOfElapsedTime: TimeInterval, endOfElapsedTime: TimeInterval) {\n    }\n    \n    func upstreamManager(_ manager: IntdashClient.UpstreamManager, didReceiveEndOfSection sectionId: Int, streamId: Int, success: Bool, final: Bool, sentCount: Int) {\n    }\n    \n}\n```\n\n#### アップストリームするデータの送信\n\n10\\. iOSデバイス内にデータを保存する場合は、計測開始時刻を `IntdashDataFileManager.setBaseTime()` で保存しておく。  \n11\\. 基準となる計測開始時刻を `IntdashClient.UpstreamManager.sendFirstData()` で送信する。  \n12\\. 送信したいデータを、 `IntdashData` のサブクラスでラップする。  \n13\\. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager.write()` でデータを保存する。  \n14\\. 生成したデータを `IntdashClient.UpstreamManager.sendUnit()` で送信する。  \n15\\. 12、(13)、14を任意の時間または回数だけ繰り返す。  \n\n```swift\nvar baseTime: TimeInterval = -1\n    \nfunc sendFirstData(baseTime: TimeInterval, streamId: Int, channel: Int) {\n    self.baseTime = baseTime\n    guard let intdash = self.intdash else { return }\n    do {\n        // 10. iOSデバイス内にデータを保存する場合は、計測開始時刻を `IntdashDataFileManager.setBaseTime()` で保存しておく。\n        try self.intdashDataFileManager?.setBaseTime(time: baseTime)\n        // 11. 基準となる計測開始時間を `IntdashClient.UpstreamManager.sendFirstData()` で送信する。\n        try intdash.upstreamManager.sendFirstData(baseTime, streamId: streamId, channelNum: channel)\n    } catch {\n        print(\"Failed to send first data. \\(error.localizedDescription)\")\n        return\n    }\n}\n\nfunc generateData() {\n    guard let streamId = self.upstreamId else { return }\n    let timestamp = Date().timeIntervalSince1970\n    if self.baseTime == -1 {\n        self.sendFirstData(baseTime: timestamp, streamId: streamId, channel: self.targetChannel)\n    }\n    let value: Int = 1\n    // 12. 送信したいデータを、 `IntdashData` のサブクラスでラップする。\n    // データの種別に関しては「詳説iSCP 1.0」を参照してください。\n    guard let data = try? IntdashData.DataInt(id: \"intdash-data-example-id\", data: Int64(value)) else { return }\n    self.sendData(data: data, streamId: streamId, timestamp: timestamp)\n}\n\nfunc sendData(data: IntdashData, streamId: Int, timestamp: TimeInterval) {\n    guard let intdash = self.intdash else { return }\n    let elapsedTime = timestamp - self.baseTime\n    guard elapsedTime \u003e= 0 else { return }\n    DispatchQueue.global().async {\n        do {\n            // 13. iOSデバイス内にデータを保存する場合は `IntdashDataFileManager.write()` でデータを保存する。\n            if let fileManager = self.intdashDataFileManager {\n                if let fileManager = self.intdashDataFileManager {\n                    _ = try fileManager.write(units: [data], elapsedTime: elapsedTime)\n                }\n            }\n            // 14. 生成したデータを`IntdashClient.UpstreamManager.sendUnit()` で送信する。\n            try intdash.upstreamManager.sendUnit(data, elapsedTime: elapsedTime, streamId: streamId)\n        } catch {\n            print(\"Failed to send data. \\(error.localizedDescription)\")\n            return\n        }\n    }\n}\n```\n\n#### アップストリーム(計測)を終了する。\n\n16\\. iOSデバイスにデータを保存している場合は、計測時間を `IntdashDataFileManager.setDuration()` で保存する。  \n17\\. 計測の終了を示すデータを送信する。  \n18\\. アップストリームを閉じる。  \n19\\. 閉じられているアップストリームを削除する。  \n20\\. intdashサーバーとの接続を終了する。(※終了する場合)  \n\n```swift\nfunc stopUpstream() {\n    guard let intdash = self.intdash else { return }\n    self.intdash = nil\n    // 16. iOSデバイスにデータを保存している場合は、計測時間を `IntdashDataFileManager.setDuration()` で保存する。\n    let now = Date().timeIntervalSince1970 // 時間管理をDate()で行っている場合\n    let duration = now-self.baseTime\n    try? self.intdashDataFileManager?.setDuration(duration: duration)\n    self.intdashDataFileManager = nil\n    \n    let group = DispatchGroup()\n    if let streamId = self.upstreamId {\n        self.upstreamId = nil\n        do {\n            // 17. 計測の終了を示すデータを送信する。\n            try intdash.upstreamManager.sendLastData(streamId: streamId)\n        } catch {\n            print(\"Failed to send last data. \\(error.localizedDescription)\")\n        }\n                    \n        if let streamIds = self.upstreamIds {\n            self.upstreamIds = nil\n            group.enter()\n            DispatchQueue.global().async {\n                // 18. アップストリームを閉じる。\n                intdash.upstreamManager.close(streamIds: streamIds) { (error) in\n                    if let error = error {\n                        print(\"Failed to close upstream. \\(error.localizedDescription)\")\n                    } else {\n                        print(\"Success to close upstream.\")\n                    }\n                    // 19. 閉じられているアップストリームを削除する。\n                    intdash.upstreamManager.removeClosedUpstream()\n                    group.leave()\n                }\n            }\n        }\n    }\n    \n    group.notify(queue: .global()) {\n        // 20. intdashサーバーとの接続を終了する。(※終了する場合)\n        intdash.disconnect { (error) in\n            if let error = error {\n                print(\"Failed to disconnect to the intdash server. \\(error.localizedDescription)\")\n            } else {\n                print(\"Success to disconnect to the intdash server.\")\n            }\n        }\n    }\n}\n```\n\n## ■ 使用している外部ライブラリ\n- [SwiftWebSocket](https://github.com/tidwall/SwiftWebSocket/blob/master/Source/WebSocket.swift)\n    - ライセンス：MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faptpod%2Fintdash-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faptpod%2Fintdash-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faptpod%2Fintdash-swift/lists"}