体の芯からあったまるアプリケーション

## 初めに
冬，寒いですよね. 

あなたの手の中にある、スマートなフォン．略してスマホ．
スマートフォンはスマートなフォンなので, ホッカイロにだってなれるはずです. 
作っていきましょう．

<hr>

### 使い方
アプリを起動します．

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431953/e0880a3b-ef63-29fc-9063-c3a242071948.png" width="300">

あったかくなるのを待ちましょう．

## あったかいiOSアプリ 
材料を紹介します．

| 材料 | 個数 |
|-|-|
| XCode |1個|
|  ペラペラのView | 1個| 
|  std::thread | 論理コアに応じた数| 
|  いい感じの処理 (意味のない処理でもOK) | 少々 |

### 処理をおこなうクラス
今回の目的はコア分いっぱいに処理を実行して体を温めることですので，GCDなど使う必要はありません．むしろ使いたくありません．そのため，C++のstd::threadで必要な数分スレッドを作ることにしました．

さてstd::threadを使うことにしましたが，2025年のアプリ開発では基本的にSwiftを用いると思います．
いままでは, SwiftからC++のクラスを呼び出すにはObj-Cでラップするなど少々めんどくさい実装が必要でした．実は素晴らしいことに，最近SwiftからC++が呼べるようになったので，これを使っていきましょう．

(C++呼べるの便利すぎる, ラッパーを書かなくていいのは最高です．AndroidのNDKはもっと最悪...たすけてくれ...)

```cpp
namespace Warmer{
class Core{
private:
    std::vector<Process*> pi_;
    
    int thread_n_ = 1;
    int current_thread_ = 0;

public:
    Core(){
        thread_n_ = std::thread::hardware_concurrency();
    }
    
    ~Core(){}
            
    void start(){
        for(int i=0; i<thread_n_; i++){
            Process* calc = new Process();
            calc->run();
            pi_.push_back(calc);
        }
    }

    // ~~~~ 中略 ~~~~~
    
    void stop(){
        for(auto e : pi_){
            e->stop();
        }
        
        for(auto e : pi_){
            delete e;
        }
        
        pi_.clear();
    }      
};
};
```

`std::thread`を持つクラス内で，無限にこの`___task`を呼びまくります．
少々の処理といいましたか，これでもかと言うほどのゴリゴリの処理を入れましょう．

とにかく和積の数を稼ぎます．`pow`関数なんかもいいですね．
コンパイラに不要と思われないように出力した値はちゃんと使った方が無難かもしれません．手抜きでprintしておきます．

ちなみに，本当に意味のない処理だと，電力が無駄ですし，CPUがかわいそうなのでモンテカルロ法で円周率も求めています．

```cpp
void Warmer::Process::__task(uint64_t cycle, double x, double y){
    std::lock_guard<std::mutex> lock(mtx_);
    
    if(cycle == 0){
        pi_count_ = 0;
        return;
    }
    
    if (x*x + y*y <= 1.0) {
        pi_count_++;
    }

    double pi = ((double)pi_count_ / (double) cycle) * 4.0;
    pi_.store(pi, std::memory_order_release);
    
    
    // なんかよくわかんないけど和積を増やそう
    double sa = 0.0;
    double ca = 0.0;
    
    int N = N_;
    int M = M_;
    
    M_ = std::max(0, M_);
    M_ = std::min(M_, MAX_M);
    
    N_ = std::max(0, N_);
    N_ = std::min(N_, MAX_N);
    
    for(uint64_t i=0; i<N; i++){
        double cy = (double) N;
        
        double s = 0;
        double c = 0;
        
        for(int j=0; j<M; j++){
            s += std::sin((double) N / 1000.0 * std::pow(2.0, j) * 1.0 * pi) * ((double)i / cy) / (double) M;
            c += std::cos((double) N / 1000.0 * std::pow(2.0, j) * 1.0 * pi) * ((double)i / cy) / (double) M;
        }                                    
                
        sa += s / cy;
        ca += c / cy;
    }
            
    dummy_ += sa + ca;
    
    if(cycle % 1000000 == 0){
        printf("%f\n", dummy_);
    }
}
```

### ペラペラのView
適当なのはご了承ください．
むしろ適当な方がいいですね．

温度のステートを細かく知ることはできませんが，定性的な状態を読み取ることは可能です．
`ProcessInfo.processInfo.thermalState`に状態が入っていますので，これを読み取って表示させましょう．

```swift
class ContentModel: ObservableObject{
    enum State{
        case normal
        case fair
        case serious
        case critical
        
        var color: Color{
            switch self{
                case .normal:
                    return .blue
                case .fair:
                    return .green
                case .serious:
                    return .yellow
                case .critical:
                    return .red
            }
        }
        
        var label: String{
            switch self{
                case .normal:
                    return "冷たいね"
                case .fair:
                    return "ちょっとあったかい"
                case .serious:
                    return "あったかい〜"
                case .critical:
                    return "あちち"
            }
        }
        
        init(_ state: ProcessInfo.ThermalState){
            switch state {
                case .nominal:
                    self = .normal
                case .fair:
                    self = .fair
                case .serious:
                    self = .serious
                case .critical:
                    self = .critical
                @unknown default:
                    self = .normal
            }
        }
    }
    
    
    
    @Published var scale: Double = 1.0
    @Published var boost: Double = 0.0
    @Published var level: Double = 0.0
    @Published var thermalState: State = .normal
    
    var core: Warmer.Core
    
    init(){
        self.core = Warmer.Core()
        self.core.start();
        var timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { _ in
            self.scale = self.core.pi() / 3.14159265358979
            self.thermalState = .init(ProcessInfo.processInfo.thermalState)
        })
    }
    
    func updateBoost(_ value: Double) {
        core.boost(value)
        objectWillChange.send()
    }
    
    func updateLevel(_ value: Double) {
        core.level(value)
        objectWillChange.send()
    }
}
```

```swift
struct ContentView: View {
    
    @ObservedObject var viewModel: ContentModel
    
    init(){
      viewModel = ContentModel()
    }
        
    var body: some View {
        VStack{
            Spacer()
            HStack{
                Spacer()
                ZStack{
                    Circle()
                        .foregroundStyle(viewModel.thermalState.color)
                        .frame(width: 300, height: 300)
                    Circle()
                        .stroke(Color.blue, lineWidth: 3)
                        .frame(width: 300, height: 300)
                        .scaleEffect(viewModel.scale)
                    Text(viewModel.thermalState.label)
                        .foregroundStyle(Color.white)
                        .font(.system(size: 18, weight: .bold))
                    
                }
                Spacer()
            }
            Spacer()
            
            HStack{
                Text("Boost")
                Slider(value: $viewModel.boost, in: 0...1, step: 0.1){ newValue in
                    viewModel.updateBoost(viewModel.boost)
                }
            }
            
            HStack{
                Text("Level")
                Slider(value: $viewModel.level, in: 0...1, step: 0.1){_ in
                    viewModel.updateLevel(viewModel.level)
                }
            }
        }
        .padding()
    }
}
```
### XCodeで10秒焼き上げます
あなたのXCodeに入れて、10秒ほど焼き(コンパイルし)ます．

## 完成！
あとは，立ち上げてまつだけ！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431953/08a8914d-82b5-7be2-4966-ac7b1f140be4.png" width="300">

けっこう暖かくなります．
温度計がないので，詳細な温度はわかりませんが ;-(

## 最後に
これで寒い冬も乗り切れます．

バッテリーが1分で1%減りますが...
(iPhone SE 3rd)


### 免責事項
バッテリーの劣化を引き起こすため，試さないでください．
試してしまっても，記事の内容にによって生じる，いかなる損害についても，私は責任を負いかねます....！