SwiftとSwiftUIで上長ダッシュアプリを作る

# はじめに
こんにちは！
普段、開発作業を行なっていると、様々な問題が発生しますよね。

進捗がやや遅れ気味とかいまいち理解が追いつかないとか、それならまだ安心。
モチベーションの低下など、メンタル面に影響すると、大変なことになります。

特にメンタル面は早期に解決した方がいいので、少しでも早くアラートを上げるべき。
しかし、どのタイミングでアラートを上げるべきか、そして何を伝えるべきか、分からなかったりします。

そこで、今回は、ボタンを押すだけでアラートをGoogleチャットに送信するシンプルなiOSアプリを作成します。

少しでも解決の一助となれば幸いです。

# 要件
- 名前を設定できる
- Webhook URLを設定できる
- 名前とWebhook URLはローカルストレージに保存され、次回起動時に利用される

# 準備
プロジェクトの作成方法は、[Swift & SwiftUIでタロットカードアプリを作る](https://qiita.com/yam_dev/items/871b47c1065f570ff17a) を参照してください。

User Interfaceは`SwiftUI`を選択しました。

### 今回のディレクトリ構成
ContentViewを修正し、UserDefaultsManagerを追加してます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/d4450059-bd0b-19c0-48ab-1d47c2fbfb59.png" width="60%" />

# 実装
 - UserDefaultsManager
UserDefaultsを用いたデータ保存
```swift:UserDefaultsManager.swift
//
//  UserDefaultsManager.swift
//  BossDash
//

import SwiftUI

struct UserDefaultsKeys {
    static let nameKey = "userName"
    static let webhookURLKey = "webhookURL"
}

class UserDefaultsManager: ObservableObject {
    @Published var name: String = ""
    @Published var webhookURL: String = ""

    init() {
        loadUserData()
    }

    func loadUserData() {
        name = UserDefaults.standard.string(forKey: UserDefaultsKeys.nameKey) ?? ""
        webhookURL = UserDefaults.standard.string(forKey: UserDefaultsKeys.webhookURLKey) ?? ""
    }

    func saveUserData() {
        UserDefaults.standard.set(name, forKey: UserDefaultsKeys.nameKey)
        UserDefaults.standard.set(webhookURL, forKey: UserDefaultsKeys.webhookURLKey)
    }
}
```

- ContentView
```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @StateObject var userDefaultsManager = UserDefaultsManager()
    @State private var isAlertPresented = false

    var body: some View {
        ZStack {
            VStack(spacing: 30) {
                Text("BossDash💨")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                    .padding()

                TextField("Enter your name", text: $userDefaultsManager.name)
                    .padding()
                    .background(Color(UIColor.systemGray6))
                    .cornerRadius(10)
                TextField("Enter Webhook URL", text: $userDefaultsManager.webhookURL)
                    .padding()
                    .background(Color(UIColor.systemGray6))
                    .cornerRadius(10)

                Button("Hey Boss, Got a sec?") {
                    sendAlert()
                    isAlertPresented = true
                    userDefaultsManager.saveUserData()
                }
                .padding()
                .frame(maxWidth: .infinity)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(10)
                .font(.headline)
                .alert("Alert Sent!", isPresented: $isAlertPresented) {
                    Button("OK", role: .cancel) { }
                } message: {
                    Text("Notification sent to your boss!")
                }

                Spacer()
            }
            .padding(.top, 100)
            .padding()
        }
        .edgesIgnoringSafeArea(.top)
    }

    func sendAlert() {
        guard let url = URL(string: userDefaultsManager.webhookURL) else {
            print("Invalid URL")
            return
        }

        let message = "Hey Boss, just a heads-up:\n\n" + userDefaultsManager.name + " has a quick message for you."

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")

        let jsonData = try! JSONEncoder().encode(["text": message])

        URLSession.shared.uploadTask(with: request, from: jsonData) { _, response, error in
            if let error = error {
                print("Error sending alert: \(error)")
            } else if let response = response as? HTTPURLResponse,
                     response.statusCode == 200 {
                print("Alert successfully sent!")
            } else {
                print("Failed to send alert.")
            }
        }
        .resume()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

# 動作確認
それでは、使ってみましょう！

シミュレーターで起動直後の状態。
名前とWebhook URLの入力欄、送信ボタンがあります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/b1dcf297-3975-cbf5-6f02-88dd984edd1a.png" width="65%" />

名前とWebhook URLを入力し、ボタンを押しました！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/3f3d04f0-3676-25c6-faa4-cec5c61814a4.png" width="65%" />

Googleチャットのスペースに通知が届いてました！
これで上長が 「なんかあった？」 と対応してくれますね😃

![スクリーンショット 2024-06-15 15.37.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/1efb4464-9057-6cd2-a64e-bb8e7f16db72.png)

なお、名前とWebhook URLは保存されるので、次回起動時には、ボタンをタップするだけで上長を気軽に召喚できます👍

# まとめ
今回は、上長に「ちょっといい？」と気軽に声をかけられるアプリを作ってみました。

ボタン一つでメッセージが飛ぶのは便利ですね。

色々と応用できると思うので、みなさんも作ってみてください🙂