【Swift】完全独学で計算機アプリを作成・リリースした話。

#はじめに
はじめまして、こなもんと申します。
プログラミングの学習を始めて約半年の初学者です。

先日、簡単な計算機アプリを独学で作り、App Storeからリリースする事ができました。
![SS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416187/b417c44c-3ea3-88ca-ee98-994e0ba1d7f3.png)
[Heat Calculator](https://apps.apple.com/jp/app/heat-calculator/id1469176036)
⬆️よかったらDLして使ってみてください…！


アプリの概要、実際に書いたコード、つまずいた箇所を説明させていただきます。
よろしくお願い致します。

#自分のバックグラウンド
Swiftに関してはProgateの講座を２週して、
テキスト「詳細！Swift iPhoneアプリ開発の入門ノート」を半分ほど読んだ程度の知識です。
分からない事はその都度テキストを参照したり、ネット検索をして解決しました。
今回のアプリは、コードを書くのに約３週間、審査まわりに約１週間かけてリリースしました。
審査は1回で通りました。

#アプリの概要
薬剤師向け、調剤用の計算機です。
簡単に言うと、÷14と÷21の計算に特化したものです。

薬剤師は処方箋を見て薬を取り揃える際、処方薬の１日量と処方日数を掛け合わせた個数を棚から取り出します。
この際、薬が10錠/1シートの包装のものであれば、計算は楽です。
(ちなみに、薬の包装に使われているプラスチックとアルミでできたシートのことをヒートと言います。以後、ヒートと表記します。)

しかし、医療の現場には14錠/ヒートだったり、21錠/ヒートのものが存在します。
このような１枚に７の倍数の錠剤が包装されたものを、我々はウィークリーヒートと呼びます。業界用語です。
そして、取揃える薬がウィークリーヒートだった場合、計算が少し難しくなります。

例えばですが、次の処方を見てください。

**ロキソニン錠60mg  1日3錠**
**１日３回　毎食後　 30日分**

この場合、１日３錠x30日分なので、必要な錠剤は90錠です。
10錠/ヒートだと９枚取りそろえればOKですが、
21錠/ヒートだとどうでしょう？

21錠/ヒートx4枚+端数6錠(84+6=90錠)ですね。ちょっとめんどくさいです。

前置きが長くなってしまいました。
このアプリでは左上の入力フォームに①１日量、右上の入力フォームに②処方日数を入力し、
③ウィークリーヒートの種類(14 or 21)を選択する。
この３つの操作だけで、

全量： ① x ②　(上記のロキソニンの例だと90錠)
ヒートの枚数： ① x ② / ③の商 (4枚)
端数： ① x ② / ③の端数　(6錠)

上記の３つの計算結果を出してくれます。
既存のものだと、まず全量を求め、そこから再度計算してヒート枚数、端数を求める必要がありました。
3つの操作だけで調剤に必要な計算結果が得られる事が、この計算機の最大の特徴、利点だと思います。

アプリの説明が長くなってしまいました。
簡単にまとめると、

①薬剤師向け
②調剤のややこしい計算を楽にする
③必要な手数を減らした計算機
と、なります。

次からいよいよコーディングに入っていきます。

#コードに関して

###全体
XCode上のView Controllerの画面です。
![スクリーンショット 2019-06-30 2.25.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416187/c8d2e9b7-5edb-e53e-490d-41a0156160e8.png)

主な要素は3種類です。
数値を入力するフォームが２つ、押すことで計算をするボタンが２種類、計算結果を表示するラベルが3つあります。

```swift:fig1
//コードを記載する場所は以下の【】の数字を参考にしてください。
import UIKit

class ViewController: UIViewController {
    //【①】
    override func viewDidLoad() {
    //【②】
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    //【③】
}
```
このコード(fig1)は、XCodeのファイルを新規作成した際にすでに記載されている、ViewController.swiftのものです。
コードをどこに記載していくかは、この図を参考にしてください。

###フォームに関する設定・説明
まず数値入力用のフォームを2つ作ります、
左上に１日量のText Field(doseField)を、右上に1日量のText Field(daysField)を作成し、Outlet接続します。
XCode上でフォームの中に入力する数値の種類(１日量と日数)を記載しました(Show the Attributes inspectorの画面でPlaceholderに記載)。

```swift
//【①】に記載
    @IBOutlet weak var doseField: UITextField!
    @IBOutlet weak var daysField: UITextField!
```

次に、全量(total)、ヒート枚数(numHeats)、端数(fraction)の計算結果出力用のLabelを作り、Outlet接続します。
画面下部に縦に並べて設置し、その左右にに数値の種類(全量・ヒート枚数・端数)とその単位を記載したLabelを配置させました。

```swift
//【①】に記載
    @IBOutlet weak var total: UILabel!
    @IBOutlet weak var numHeats: UILabel!
    @IBOutlet weak var fraction: UILabel!
```

###ボタンに関する設定・説明
最後に、画面中央に÷14の処理をするButtonと÷21の処理をするButtonを作成し、Action接続します。
÷14だけ記しますが、÷21のものもこれと全く同じです(14の部分を21に変えただけです。)

```swift
//【①】に記載
//  14を押した時の処理　①dose x daysで全量　②ヒート枚数　③端数
    @IBAction func btn14(_ sender: Any) {
        
//      【A】テキストフィールドに入力された値を定数化
        let dose:Int? = Int(doseField.text!)
        let days:Int? = Int(daysField.text!)
           
//      【B】合計への出力 ?? 1 で初期値を１にしている
        let outputTotal = Int((dose ?? 0) * (days ?? 0))
        if outputTotal>5000 {
            total.text = "ERROR"
        }else{
            total.text = String(outputTotal)
        }
        
//      【C】 ヒート枚数への出力
        let outputnumHeats:Double = Double(((dose ?? 0) * (days ?? 0))/14)
        let outputnumHeatsfloor:Int = Int(floor(outputnumHeats))
        
        if outputTotal>5000 {
            numHeats.text = "ERROR"
        }else{
            numHeats.text = "14 × " + String(outputnumHeatsfloor)
        }

//      【D】端数への出力
        let outputFraction:Int = Int(((dose ?? 0) * (days ?? 0))%14)
        if outputTotal>5000{
              fraction.text = "ERROR"
        }else{
              fraction.text = String(outputFraction)
        }

//      【E】ボタン押したらキーボード下げる
        view.endEditing(true)
    }
```

【A】まずTextFieldに入力された値を定数に代入します。
doseFieldに入力されたテキストを定数doseに、
daysFieldに入力されたテキストを定数daysに代入します。
コードとは別に、XCodeの設定で(Show the Attributes Inspector)、Keyboard Type をNumber Padにして、数字のみ入力できるようにしています。

入力された数値を計算式に当てはめ、３種類の計算を行います。

【B】 dose * days
定数doseと定数daysを掛けた値を定数outputTotalに代入します。
その後、計算結果をif文を用いて条件分岐させ、問題なければtotalのLabelに出力させます。

初め、TextFieldに値を入れていない状態でボタンの処理をすると、XCodeが落ちてしまい悩みました。
二つの定数に初期値を設定し（定数 ?? 0 の形で）解決しました。

if文を用いた理由ですが、計算結果が実際の業務において現実的ではない数値(とりあえず>5000としました)だった場合に、エラーである旨を表示したかったからです。
本当は入力する値の範囲を決めることで(1日量は1~12のような感じ)、現実的でない値を除外したかったのですが、分からなかったのでこのような方法を用いて無理やり解決させました。

定数に初期値を与える事と、if文で結果を分岐させる事に関しては、以下の2つの計算においても同じです。

【C】 (dose * days) ÷ 14の商
上記の計算式を、まずDoubleの形で定数outputnumHeatsに代入します。
その後、outputnumHeatsをfloor()で整数にし、Int型のoutputnumHeatsfloorに代入します。
if文を用い、numHeatsのLabelに出力します。
ボタンを押した後に、14と21どちらのボタンかわかりやすいように、"14 x "のテキストも添えて出力させています。

【A】 (dose * days) ÷ 14の端数
(dose * days) % 14で端数を求め、Int型の定数outputFractionに代入します。
その後if文を用い、問題なければfractionのLabelにoutputFractionの値を出力します。

【E】最後に、１４・２１ボタンを押したあと、計算結果が見えやすいようにキーボードを下げるコードも入れました。


計算の機能に関するコードは以上です。
...ちなみに作成後に思いついたのですが、if文を以下のように記述すればよりスッキリ書けたと思いました。

```swift
        if outputTotal>5000 {
            total.text = "ERROR"
            numHeats.text = "ERROR"
            fraction.text = "ERROR"
        }else{
            total.text = String(outputTotal)
            numHeats.text = "14 × " + String(outputnumHeatsfloor)
            fraction.text = String(outputFraction)
        }
```

###デザインに関する設定・説明

次に、ボタンやラベルに色などのデザイン要素を設定します。
デザイン用に、ボタン2つとラベル３つを再度Outlet接続しました。

```swift
//  【①】に記載
    @IBOutlet weak var btn14: UIButton!
    @IBOutlet weak var btn21: UIButton!
    
    @IBOutlet weak var totalLabel: UILabel!
    @IBOutlet weak var numHeatsLabel: UILabel!
    @IBOutlet weak var fractionLabel: UILabel!
```

次に、ボタンやラベル、背景の色、ラベルの形を設定します。


```swift
//      【②】に記載

        //色の設定
        let bgcolor = UIColor(red: 180/255, green: 214/255, blue: 211/255, alpha: 1.0)
        let btncolor1 = UIColor(red: 135/255, green: 179/255, blue: 141/255, alpha: 1.0)
        let btncolor2 = UIColor(red: 48/255, green: 197/255, blue: 255/255, alpha: 1.0)
        
        //　背景の色
        view.backgroundColor = bgcolor
        
        //　btnの色
        btn14.backgroundColor = btncolor1
        btn14.setTitleColor(UIColor.white, for: .normal)
        btn21.backgroundColor = btncolor2
        btn21.setTitleColor(UIColor.white, for: .normal)

        // ラベル枠を丸くする
        totalLabel.layer.masksToBounds = true
        // ラベル丸枠の半径
        totalLabel.layer.cornerRadius = 10
        
        numHeatsLabel.layer.masksToBounds = true
        numHeatsLabel.layer.cornerRadius = 10
        
        fractionLabel.layer.masksToBounds = true
        fractionLabel.layer.cornerRadius = 10
```
まず、色をRGBAで指定し、定数に代入しました。
Swiftでは○○○/255といった感じでRGBを指定するようです。（少しつまづきました）

上記の色の定数を、.backgroundColorで背景とボタン２つに代入しました。
ボタン内の文字色(白色)は.setTitleColorで設定しました。
（ここの色もRGBAで指定して代入しようとしたのですがうまくいかず、上記のコードを使用しました。）

最後に、ラベルの枠の丸さを設定しています。

###その他コード
Tap Gesture Recognizerを接続し、以下のコードを加える事で、
画面をタップした時にキーボードを引っ込めることができます。

```swift
   //【③】に記載
   @IBAction func tapView(_ sender: UITapGestureRecognizer) {
        view.endEditing(true)
    }
```

以上がViewController.swiftに記載したコードの全てです。
他にもXCode上でレスポンシブデザインの要素をいじったりしていますが、その辺りはこの記事では省略させていただきます。

#審査提出後の流れ

6/24 の14:30ごろに審査に提出しました。
6/25 の朝起きた時に承認のメールを確認しました。
![mail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416187/292603a6-a9b9-749a-33ae-34a17478178a.png)
...とても興奮しました。
その後しばらくはダウンロードできなかったのですが、その日の19時ぐらいにAppStoreでDLできました。
（DLができるようになった旨のメールはなかったです。）

#参考記事

最後に、審査関係などプログラミング以外で役に立ったブログやサイトを紹介します。

アプリの登録・申請・リリースまでの流れについて、この記事を参考にしました。とても分かりやすかったです。
https://i-app-tec.com/ios/app-release.html

アプリをリリースする際に、アイコンを作成し、登録する必要があります。
以下の記事を参考にして、アイコンを自作しました。
https://qiita.com/seihmd/items/25f2a42e20e88ea5d86f

アプリをApple Developer Programに登録する際、画面のスクショを登録する必要があるのですが、サイズが合わず弾かれて困っていました。
以下のサイトで、画像のサイズを指定、変換して用いました。
https://www.peko-step.com/

#最後に
これでこの記事は終わりです。

まだプログラミングに関しても、Qiitaの投稿に関しても初心者ですので、至らない点が多いかと思います。
何か気づきがありましたら、アドバイスいただけると幸いです。

最後まで読んでいただき、ありがとうございました。

以上です。ではまた！