100 Days of SwiftUI(DAY 60(Project10〜12))

あなたが学んだこと
What you learned

これらの最後の3つのプロジェクトは、最初はインターネットを使用してデータを送受信することから始まり、次にCore Dataに移動して、実際のアプリがどのようにデータを管理するかを確認できるように、データに本当に力を入れています。このプロジェクトで学んだスキルは、おそらくあなたが理解するより重要です。それらをすべてまとめると、インターネットからデータをフェッチし、ローカルに保存し、ユーザーがフィルターにかけて関心のあるものを見つけることができるからです。

過去3つのプロジェクトで取り上げたすべての新しいことを簡単にまとめます。

カスタムCodable適合性の構築
を使用したデータの送受信 URLSession
disabled()ビューの修飾子
を使用してカスタムUIコンポーネントを構築する @Bindable
AnyView型消去に使用
alertアラートに複数のボタンを追加する
SwiftのHashableプロトコルがSwiftUIでどのように使用されるか
使用する@FetchRequestコアデータを照会するプロパティラッパーを
を使用したコアデータ結果の並べ替え NSSortDescriptor
カスタムNSManagedObjectサブクラスの作成
を使用したデータのフィルタリング NSPredicate
コアデータエンティティ間の関係の作成Creating relationships between Core Data entities

コアデータはNSSetでは難しいです、橋渡しする必要があある。

AnyViewとグループ:実際の型消去
AnyView vs Group: type erasure in practice

ビューのタイプを動的に決定することはできません。つまり、テキストビューとイメージを返すことができない場合があります。
Function declares an opaque return type, but the return statements in its body do not have matching underlying types

struct ContentView: View {
    var body: some View {
        if Bool.random() {
            return Text("Hello, World!")
                // ↓で不可となる。
                .frame(width: 300)
        } else {
            return Text("Hello, World!")
        }
    }
}

AnyView

struct ContentView: View {
    var body: some View {
        if Bool.random() {
            return AnyView(Text("Hello, World!")
                .frame(width: 300))
        } else {
            return AnyView(Text("Hello, World!"))
        }
    }
}

使用にはパフォーマンスコストがかかり、ビューの階層が変更されたときにSwiftUIにさらに多くの作業を強制します。

代替案
alternative
Group
私にとってグループは以下のために使用されます:

10子ビューの制限を打ち破る:各グループは独自の10の子を持つことができるため、グループ内にグループを作成して、より複雑なレイアウトを作成できます。
レイアウトを親コンテナに委任しています。あなたはその本体のトップレベルのものとしてグループを持つカスタムビューを作成する場合は、内部ビューことを埋め込むことができますHStackまたはVStack動的にレイアウトを変更します。
1組のモディファイアを一度に多くのビューに適用してみましょう。

struct ContentView: View {
    var body: some View {
        Group {
            if Bool.random() {
                Text("Hello, World!")
                    .frame(width: 300)
            } else {
                Text("Hello, World!")
            }
        }
    }
}

この種のコードは、実際の型消去でのみ可能です。

struct ContentView: View {
    @State var views = [AnyView]()

    var body: some View {
        VStack {
            Button("Add Shape") {
                if Bool.random() {
                    self.views.append(AnyView(Circle().frame(height: 50)))
                } else {
                    self.views.append(AnyView(Rectangle().frame(width: 50)))
                }
            }

            ForEach(0..<views.count, id: \.self) {
                self.views[$0]
            }

            Spacer()
        }
    }
}

コード化可能なキー
Codable keys

import UIKit


struct User: Codable {
    var firstName: String
    var lastName: String
}

let str = """
{
    "firstName": "Andrew",
    "lastName": "Glouberman"
}
"""

let data = Data(str.utf8)

do {
    let decoder = JSONDecoder()

    let user = try decoder.decode(User.self, from: data)
    print("Hi, I'm \(user.firstName) \(user.lastName)")
} catch {
    print("Whoops: \(error.localizedDescription)")
}

convertFromSnakeCase

import UIKit


struct User: Codable {
    var firstName: String
    var lastName: String
}

let str = """
{
    "first_name": "Andrew",
    "last_name": "Glouberman"
}
"""

let data = Data(str.utf8)

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase

    let user = try decoder.decode(User.self, from: data)
    print("Hi, I'm \(user.firstName) \(user.lastName)")
} catch {
    print("Whoops: \(error.localizedDescription)")
}


import UIKit


struct User: Codable {
    enum CodingKeys: String, CodingKey {
        case firstName = "first"
        case lastName = "last"
    }

    var firstName: String
    var lastName: String
}

let str = """
{
    "first": "Andrew",
    "last": "Glouberman"
}
"""

let data = Data(str.utf8)

do {
    let decoder = JSONDecoder()
    //decoder.keyDecodingStrategy = .convertFromSnakeCase

    let user = try decoder.decode(User.self, from: data)
    print("Hi, I'm \(user.firstName) \(user.lastName)")
} catch {
    print("Whoops: \(error.localizedDescription)")
}