「SwiftUI」 ForEachでidを設定しつつindexを取得する方法

ForEachで同じViewを繰り返そうとした時に回した回数(index)を知りたかったケースがあったので、メモとして残しておきます。

先にサンプルのコードを記載しておきます。

struct TestView: View {
    var languages = ["Swift", "Ruby", "Python"]
    var body: some View {
        VStack {
            ForEach(Array(languages.enumerated()), id: \.offset) { index, language in
                Text("index \(index) : language \(language)")
            }
        }
    }
}

ForEach文のindexにインデックス番号、languageにインデックス番号に対応する要素が入ります。 「id: \.offset」はデータを一意に識別するためのキーとしてインデックス番号を指定しています。 インデックス番号でなく、要素(ここではlanguage)をキーとしたい場合は、id: \.elementとしてください。

ちなみにですが、enumerated()が何をしているのかというと、こんな感じです。

var languages = ["Swift", "Ruby", "Python"]
for (index, language) in languages.enumerated() {
    print("\(index): '\(language)'")
}

// 出力結果
0: 'Swift'
1: 'Ruby'
2: 'Python'

公式ドキュメントはこちらです。 developer.apple.com

「良いコードを書く技術」第1章の感想

最近SwiftUI を利用したIphoneアプリをリリースしました(お気に入り貯金と検索すると出てきます)。初めてアプリをリリースできたので嬉しかったのですが、設計をほとんど考えず好き勝手にコードを書いたので非常にメンテナンスがしにくいです。趣味ならいいですがこれでは仕事で通用しないと危機感を抱いたので、コードを書く時の基本をしっかり身につけようと本書を購入しました。少しずつ読み進めているのでそれぞれの章ごとに感想を書いていきたいと思います。今回は第1章です(自分用のメモみたいな感じで書くので見てる方のお役に立つか分かりません😢)

 

第1章のテーマは「良いコードの定義と価値」です。まずは定義ですが、筆者は次の4つを満たすものを良いコードとしています。

・保守性が高い

・すばやく効率的に動作する

・正確に動作する

・無駄な部分がない

 

1 保守性が高い

「将来の自分は記憶力において、他人と同然です」と本書に書かれています。

つまり、他人が見て理解できるコードでないと数ヶ月後に自分が見直しても分からないということですね。僕もよく数ヶ月前のプログラムを見返したら、「これ何のための処理だっけ?」となることが多いので一番改善したいポイントです。分かりやすい変数名をつける、機能を分割する等を徹底して他人が読みやすいコードを意識することが大切ですね。

 

2 すばやく効率的に動作する

1章では詳しく書かれていませんでした。メモリ管理やアルゴリズムを意識した設計のことですね。ゆくゆくはパフォーマンスを考慮できるレベルになりたいです。

 

3 正確に動作する

正常な値が来るはずと決めつけずに、不正な値が来ても被害を受けないようにする。

防御的プログラミングというそうです。開発に慣れていないと、自分の頭の中にある値が来る前提でコードを書いちゃうので、エラーが発生してもプログラムが止まらない処理を書かないといけないですね。(個人的にはSwiftで強制アンラップしたら中身がnilでクラッシュ!みたいなこと結構やっていたので気をつけたいと思います。)

 

4 無駄な部分がない

「抽象化」することで無駄な繰り返しを避ける。(第十章で説明)

 

開発者が良いコードを書くこのメリット

ここでは3つ挙げられています。

・プロジェクトを強力に推し進める

プログラマーとしての評価が高まる

・仕事に満足感や自信が持てるようになる

 

どれも魅力的なメリットです。プログラミングの学習を始めた初期は、エンジニアは技術さえあればいいんだ!という思考があったのですが、実際に業務を経験してみると社内の目標を達成するために何が必要か見極める力や、他の部署の人と協力するコミュニケーション力、非エンジニアの人に技術をわかりやすく説明する力等色々必要でした。

技術は目的ではなくツールであるということも忘れないようにしたいです。

 

 

 

 

プライバシーポリシー(嘘電話)

はじめに

このページでは、あなたの情報を嘘電話(以下「本アプリ」と称します。)がどのように取り扱うかを説明します。本アプリのご使用によって、本規約に同意していただいたものとしてみなします。

 

あなたの情報

本アプリは、個人を特定するために利用できる一切の個人情報を、収集又は補完しません。

一方、本アプリは、以下の情報をあなたのデバイス上で使用及び保管します。

  ● 登録した「名前」「電話番号」「読み上げる文章」

以上の情報は暗号化されません。

本アプリは、以上の情報をデバイスから転送せず、いかなるウェブサイト又はサービスとも共有しません。

 

免責事項

本アプリあユーザーの特定の目的に適合すること、期待する機能・正確性・有用性を有すること、及び不具合が生じないことについて、何ら保証するものではありません。

当方の都合によりアプリの使用を変更できます。私たちは、本アプリの提供の終了、変更、又は利用不能、本アプリの利用によるデータの消失又は機械の故障もしくは損傷、その他本アプリに関してユーザーが被った損害につき、賠償する責任を一切負わないものとします。

 

お問い合わせ

お問い合わせフォーム

プライバシーポリシー(お気に入り貯金)

はじめに

このページでは、あなたの情報をお気に入り貯金(以下「本アプリ」と称します。)がどのように取り扱うかを説明します。本アプリのご使用によって、本規約に同意していただいたものとしてみなします。

 

あなたの情報

本アプリは、個人を特定するために利用できる一切の個人情報を、収集又は補完しません。

一方、本アプリは、以下の情報をあなたのデバイス上で使用及び保管します。

 ● 初期設定で選択した買いたいものの画像(選択した画像はホーム画面の「ファイル」⇨ 「iphone内」⇨ 「SavingApp」に保存され、開発者側に情報が送信されることはありません。)

 ● 買いたいものの名前

 ● 買いたいものの値段

 ● 1ヶ月の目標貯金額

 ● 現在の貯金額

以上の情報は暗号化されません。

本アプリは、以上の情報をデバイスから転送せず、いかなるウェブサイト又はサービスとも共有しません。

 

免責事項

本アプリあユーザーの特定の目的に適合すること、期待する機能・正確性・有用性を有すること、及び不具合が生じないことについて、何ら保証するものではありません。

当方の都合によりアプリの使用を変更できます。私たちは、本アプリの提供の終了、変更、又は利用不能、本アプリの利用によるデータの消失又は機械の故障もしくは損傷、その他本アプリに関してユーザーが被った損害につき、賠償する責任を一切負わないものとします。

 

お問い合わせ

お問い合わせフォーム

RealmSwift 「Instance method 'sorted(by:)' requires that 'SortDescriptor' conform to 'Sequence'」エラーについて

Realmでデータを取得する際にソートをかけようとしたらエラーに出会いました。 原因はソートの条件(sortProperties)を配列で指定しなかったからでした。

失敗例↓

let sortProperty = SortDescriptor(keyPath: "date", ascending: false)

成功例↓

let sortProperty = [SortDescriptor(keyPath: "date", ascending: false)]

ソートの条件が一つでも[]で囲みましょう。

開発中に@Appstorage(UserDefaults)のデータを全て削除する

アプリの開発中にUserDefaultsのデータを削除したい場合があり、Xcodeの機能で消せないかなと検索したのですがどうやらなかったので、適当にボタンを作って押したらデータを削除するという方法を取りました。


Button(action: {
  let appDomain = Bundle.main.bundleIdentifier
  UserDefaults.standard.removePersistentDomain(forName: appDomain!)
})  {
  Text("データ削除")
}


appDomain定数には「com.~.sampleProject」のようにバンドルIDが格納されています。 .removePersitentDomain()は引数に渡したドメイン(バンドルID)のコンテンツを削除します。

このコードを好きなViewに記述し、ボタンを押せばデータが削除されます。

※全てのUserDefaultsのデータが削除されるので、特定のデータを消したい場合は「UserDeafaults.standard.removeObject」等で検索してください。

「SwiftUI」 @Appstorageを使って画像(UIImage)を保存する方法

データベースを使うほどではないが、画像を保存したいケースがあり、@Appstorageを使ってUserDefaultsに保存したので備忘録として残しておきます。

コード全文

ContentView.swift

import SwiftUI

struct ContentView: View {
    @AppStorage("testImage") var testImage: Data?
    let image: UIImage? = UIImage(named: "sample")
    var body: some View {
        Button(action: {
            if let saveImage = image {
                // pngData()はData型を返す
                testImage = saveImage.pngData()
            }
        }) {
            Text("画像を保存します")
        }
    }
}

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

SecondView.swift

import SwiftUI

struct SecondView: View {
    var body: some View {
        VStack {
            if let uiimage = UIImage(data: UserDefaults.standard.data(forKey: "testImage")!) {
                Image(uiImage: uiimage)
            }
        }
    }
}

struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView()
    }
}

はじめに

現在@Appstorageを使って保存できるデータ型は

・String ・Int ・Bool ・Double ・Data

になっています。

なので、画像はそのままだと保存できないためData型に変更して保存します。 UIImage ↔︎ Data

コード解説

@AppStorage("testImage") var testImage: Data?
    let image: UIImage? = UIImage(named: "mac")

まずはテスト用の画像を用意してAssets.xcassetsに保存してください。 その後、UIImage(named: "ここに画像の名前")の箇所に保存した名前を入れてください

@AppstorageにはData型で保存するのでData?です。

var body: some View {
        Button(action: {
            if let saveImage = image {
                // pngData()はData型を返す
                testImage = saveImage.pngData()
            }
        }) {
            Text("画像を保存します")
        }
    }

ボタンを押したら画像がUserDefaultsに保存されるようになってます。 pngData()はimageをData?型に変換して値を返します。 これでUserDefaultsに保存されたので他のファイルからも使用することができます。

SecondView.swift

if let uiimage = UIImage(data: UserDefaults.standard.data(forKey: "testImage")!) {
                Image(uiImage: uiimage)
            }

UserDefaultsにはData型で保存されているのでそのままでは使えません。 なのでUIImage()で型を変換しています。

これで画像が表示されているはず。

以上です。