見出し画像

【じっくりSw1ftUI 23】Essentials第10章〜Playgroundで遊ぼう⑨Swiftオブジェクト志向プログラミング❷Class(クラス)LazyStoredProperties以降

さてと、前回

がクラスの基本までやったので、今回は

LazyStoredProperties

をやってく〜〜〜🕺

さっそく、第10章の後半をじっくり読んでく

まず、Lazyとは何ぞや?なんだけど

//Lazy以降
var myAge10_2 = 43
//普通のクラスだと、
class MyAge10_2{
    var myTitle: String

}

init()がないと、、、

てな感じで、赤い警告エラー(=改修の余地あり)が発生
どうやら、初期値がないと言ってる様子👀
Fixをクリックすると、
てな感じで、解消はできるんだけど、、、
なんか気持ち悪い
なので元に戻して、initを入力すると、
予測変換で候補が出てくるので、
てな感じで、解消できた
イニシャライザで明示をしてあげた方が、可読性も上がるしね。

で、イニシャライザをクラス内で宣言する方法だけではなく、

クロージャを使って

//Closure(クロージャ)を使ってみる
class MyAge10_3{
    var myAge10_3: String = {
        var myResource = resourceIntensiveTask()
        myResource = processData(data: myResource)
        return myResource
    }()
}

みたいな書き方もできるってサンプルもあるんだけど、急に

resourceIntensiveTask()
processData(data: myResource)

てのが急に出てきて〜〜〜

てな感じで赤のFatalエラー(致命的でこのままだと自動改修不可)が出てきたので

色々調べたんだけど、サンプルが悪すぎるので、、、

元々、こちらで以前に勉強していた教材なんかでわかり安い解説がないかと探してみて〜〜〜

にあったので。。。

遅延格納型プロパティ(lazy stored property)とは

プロパティの値を遅延評価で決定するクラス、または構造体のインスタンスプロパティのうち、格納型プロパティには、初期化の際には値を決めず、必要とされた時に初めて値を決定するという挙動を指定できる。
→プロパティとして定義した値を全て使うばかりではないし、計算コストを考えれば、必要な時まで決めないのは賢明
*タイププロパティは元々そんな値の決定の仕方

格納型プロパティの先頭にlazyを置く。
変数の型は明記しておいた方が安全。
定義できるのは、クラスか構造体の内部のみ。
プロパティの遅延評価=インスタンス生成後、インスタンス自体の値を変更する
👉定数ではなく、変数で宣言が必要。

・構造体の場合
そのプロパティの値を参照することによって遅延評価を引き起こす可能性のあるメソッドにはmutatingを付ける必要あり。

・遅延格納型プロパティには、プロパティオブザーバを設定できない。
・複数のスレッドが同時にプロパティにアクセスする状況で、初期化が一度だけ行われる保証はない。

てな感じで、

//じっくり23で追加
import Foundation

//ファイルの属性を遅延評価によって取得
class FileAttribute10_3{
    let fileName: String
    //遅延評価で値を決定
    lazy var size: Int = self.getFileSize()
    init(file: String) {
        fileName = file
    }
    func getFileSize() -> Int{
        //構造体の初期化
        var buffer = stat()
        //stat呼び出し
        stat(fileName, &buffer)
        //動作確認のための印字
        print("[getFileSize]")
        //得られた値をInt型に変換
        return Int(buffer.st_size)
    }
}

//実行
let d10_3 = FileAttribute10_3(file: "text.txt")
print(d10_3.fileName)
print(d10_3.size)
print(d10_3.size)

で実行すると、

まずは実行できた

次にクロージャ版で〜〜〜

//じっくり23で追加
import Foundation
//ファイルの属性を遅延評価によって取得クロージャ版
class FileAttribute10_4{
    let fileName: String
    //クロージャ版
    lazy var size: Int = {
        var buffer = stat()
        stat(self.fileName, &buffer)
        print("[getFileSize]")
        return Int(buffer.st_size)
    }() //ここの()は必須
    init(file: String){
        fileName = file
    }
}
//実行
let d10_4 = FileAttribute10_4(file: "text.txt")
print(d10_4.fileName)
print(d10_4.size)
print(d10_4.size)
同じ結果になったことを確認

なんかを参考にすると、、、

lazyを付けることで、処理が一回みたいな例も載っているんだけど、、、

この記事の例は、構造体(struct)だし、今回の記事はClassの話で、

同じように、lazyを外すと

で実は、エラーになっちゃうんだよね〜〜〜

で、すぐ次にやるselfが邪魔してるようだから、
じゃあselfを外せばいいんじゃないか?
で安易にselfを削除すると、、、

今度はfileNameなんてメンバーはないと怒られ、
さらに.を外してみると、プロパティイニシャライザの中で使えや!って怒られるんだよね〜〜〜

ま、なので、

しっかり理解せずに安易に、コードを修正すると、

まさに、

にハマってしまうし、
実は複雑なコードでどうしても遅延バインディングをしないといけない時に、lazyを使うくらいな感覚で、ここは済ませて次に進んだ方がいい。

だから

の本文なんかで、

独学プログラミングのコツは紹介してるんだけどね💦👀

1周目でわからないことがあるくらい当たり前だし、
何かエラーがある度に全て一度で理解しようなんてしていたら、

他の理解できることを捨ててるようなもの
👉まさに、

木を見て森を見ず

になるからね。

ちなみにこの本で、

lazy

ってキーワードで本全体を検索してみたんだけど、

LazyGridLayout

なんかでヒットするだけで、Syntax以外でもほぼサンプルコードは出てこないみたいだから、

lazy =遅延バインディングで使う

だけを理解しておけばここでは十分。遅延バインディングすら、

複雑なURLSessionなんかでしか使うことがないからね。
👉いっぺんで理解しようとすることがそもそも挫折の原因
(オイラは記事を書いてる手前、2時間くらい調べたりはしてる程度。
そもそも1回で全てを理解してるとか、まだ、実践のコードを書いてもないのに、この本のコードを全て1回で暗記や理解するエンジニアなんていないし、それが出来たからって何の意味があるのかを逆に知りたいけどね👀)

目的は、

  • iOS17を学び直しがてら理解しつつ、

  • 自分の作りたいアプリが最低限作れるようになればいいだけ

だからね。なので、以前の記事でも書いたけど、

自分が学ぶ目的は何かをしっかり見定めた上で学ぶことが大事

って書いた次第。ま、それでも

  • それくらいは理解するのがプロのエンジニアだ

  • これくらい1回で理解できないと優秀なエンジニアになれない

みたいな勝手なエンジニア像を思い描いて邁進したいなら、勝手にどうぞ。
そんなもん現場を知らない素人のただの思い込みだし、

ほぼ実務でも最初のうちは使わないモノまで全て一回で理解しようなんてやってる人も過去に見たことあるけど、ほぼほぼ挫折してる人ばかりだからね。
👉自分から挫折しにいくって愚か者としか言いようがないでしょ👀💦

さてと、では次のselfをやってく〜〜〜

self:自分自身

なんだけど、これってプログラミングでも一緒で、要は、

クラス自身の中の何かを指すときに使う

ってイメージで十分。実際、前回使ったコード

class ProfileAccount10_1 {
    //ストアドプロパティ
    var m_KakuAge: Int = 43
    var m_KakuWeight: Float = 68.9//キログラム
    var m_KakuHeight: Float = 1.7//メートル
    var himanFlg: Bool = false
    var notFatFlg: Bool = true
    //コンピューテッドプロパティ:BMI計算
    var calcBMI: Float {
        get {
            return m_KakuWeight / (m_KakuHeight * m_KakuHeight)
        }
        set(resultBMI) {
            if resultBMI > 25 {
                himanFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //コンピューテッドプロパティ:肥満未満の体重上限
    var nonFatMyWeight: Float {
        get {
            return 25 * (m_KakuHeight * m_KakuHeight)
        }
        set(weightFatResult) {
            if weightFatResult - m_KakuWeight > 0 {
                notFatFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //イニシャライザ
    init(m_KakuAge: Int, m_KakuWeight: Float) {
        self.m_KakuAge = m_KakuAge
        self.m_KakuWeight = m_KakuWeight
    }
    //デイニシャライザ
    deinit{
        print("deinit")
    }
    //タイプメソッド
    func msgMyProfile(){
        print("オイラは\(m_KakuAge)のおっさんだぞ")
        print("オイラの体重は\(m_KakuWeight)だよ。運動せんと、、、💦")
    }
    //インスタンスメソッド
    class func getMinimunWeight() -> Float {
        return 49.9
    }
}

のイニシャライザ部分で、selfで、先に定義してる変数を使ってるし、さっきのコード

//ファイルの属性を遅延評価によって取得クロージャ版
class FileAttribute10_4{
    let fileName: String
    //クロージャ版
    var size: Int = {
        lazy var buffer = stat()
        stat(self.fileName, &buffer)
        print("[getFileSize]")
        return Int(buffer.st_size)
    }() //ここの()は必須
    init(file: String){
        fileName = file
    }
}
//実行
let d10_4 = FileAttribute10_4(file: "text.txt")
print(d10_4.fileName)
print(d10_4.size)
print(d10_4.size)

でも、先に定義した定数を使ってるでしょ👀

self自体の簡単なサンプルコード

を作るならば、

class MyCalc10_5{
    var myNum = 10
    func minus1() {
        self.myNum -= 1
    }
}

てな感じで、クラスの中の引数を再利用する時に使う感じで〜〜〜

この本を丸々載せてるサイト

を発見したので、そこから次のコードを見てみると、さっきのクロージャでlazyのサンプル見たいなサンプルで

//クロージャ式内からプロパティまたはメソッドを参照する場合
document?.openWithCompletionHandler({(success: Bool) -> Void in
    if success {
        self.ubiquityURL = resultURL
    }
})

みたいだけど、これをまんまPlayground書いてみると、

てな感じでもちろんエラーになるからね〜〜〜
引数とかオプショナルとかまで含まずに抜粋してるから当然な話。

で、他にも

関数パラメーターがクラス プロパティと同じ名前を持つ場合など、あいまいさを解決するために self を使用することも必要

ってことで、

class MyCalc10_6 {
    var myNum = 10
    func minus1(myNum: Int) {
        print(myNum)
        print(self.myNum)
    }
}

て感じで、明示することできるよってゆーてるみたいね👀💦

その後、

他のほとんどの状況で self を使用するかどうかは、
主にプログラマの好みの問題です。

って書いてんだけど、これが実は、

てな感じで、解消はできるんだけど、、、
なんか気持ち悪い

てゆーてたことに繋がるんだよね〜〜〜だって、

好みの問題だもん
👉職場とかでやる場合でも、他の人との兼ね合いで、selfを使ったほうがわかりやすいからコーディング規約を作ったり、個人でやる場合でも、イニシャライザすらない方が好きって場合もあるしね〜〜〜

必ずこうしないといけない=正解

を求める人は、オイラたちの仕事が

ベストプラクティス=最適解を求める仕事

って気づかない、ゆーても自分が正しいと理解しようとしないから

実はエンジニアに向いてないって人も多いしね〜〜〜

プロトコル

最近の新しいプログラミング言語では結構、使われてるヤツで〜〜
さて、プロトコルとは何ぞやなんだけど、

プロトコル:
クラスが満たさなければならない最小要件を定義する一連のルール

  • protocolキーワードを使用して宣言

  • 準拠するためにクラスに含める必要があるメソッドとプロパティを単純に定義

  • クラスがプロトコルを採用していても、プロトコル要件をすべて満たしていない場合、クラスがプロトコルに準拠していないことを示すエラーを報告

って感じらしい👀

さっき出した、詳解Swiftの解説に比べてなんて簡潔なんだ😭

さてと、プロトコルを定義

protocol M_KakuProfile10_7{
    var name: String{ get }
    func msgBuild() -> String
}

定義したプロトコルは、解説どおり何かに連携させればいいので〜〜〜

class MKakuProfile10_7: M_KakuProfile10_7{
    
}

で連携は出来たんだけど、これだけだと、

必要なstubsが足りなくないかの赤丸エラーが出てくるので〜〜〜
FIXちゃんを押してもまだ出てくるね👀
理由はなんだ?
イニシャライザがないぞと言ってんね💦
initializerちゃんを追加してあげよう
ほい、消えましたと。
で黄色三角エラーは、タイププロパティの中に処理がまだないから出てるだけなんで
みたいにしてあげるだけで完成

出来上がったコード

class MKakuProfile10_7: M_KakuProfile10_7{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgBuild() -> String {
        "オイラの名前は、" + name
    }
}

てな感じ

不透明な戻り値の型

と、プロトコルが出てきたついでに、戻り値の型の説明があるので〜〜〜

特定の戻り値の型 (具象型とも呼ばれます) を指定する代わりに、不透明な戻り値の型を使用すると、関数が指定されたプロトコルに準拠している限り、任意の型を返すことができる。
不透明な戻り値の型は、プロトコル名の前にsomeキーワードを付けることによって宣言される。

👉要はsomeを付ければいいって言ってんね👀

じゃ、実際にコードで〜〜
別のクラスをわざわざ作るのがめんどくさいから直前のコードに追加

class MKakuProfile10_7: M_KakuProfile10_7{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgBuild() -> String {
        "オイラの名前は、" + name
    }
    //不透明な戻り値 Opaque return type
    //Equatableプロトコルを使うみたいだね
    func tripleCalc(value: Int) -> some Equatable{
        
    }
}
これだけだと、不透明な戻り値云々の前の、戻り値がないぞ〜〜〜
って言ってるので、
    //不透明な戻り値 Opaque return type
    //Equatableプロトコルを使うみたいだね
    func tripleCalc(value: Int) -> some Equatable{
        value * 3
    }

って計算式なんかをやって、戻り値を返す処理を組み込んであげると

エラーは消えたね👀
  • Swift で提供される標準プロトコルである Equatable プロトコルに準拠するには、型は基礎となる値が等しいかどうか比較できるようにする必要あり。

  • ただし、不透明な戻り値の型は、自分で作成したプロトコルを含め、あらゆるプロトコルに使用できる。

ほうほう🧐って感じだね。

    //Equatableプロトコルを使うみたいだね
    func tripleCalc(value: Int,str: String) -> some Equatable{
        value * 3
        return str + "様"
    }

みたいなこともできるってことを言いたいみたい。

てな感じでね。

で、それが出来るからって何なん?って感じなんだけど具体的な戻り値の型をマスクすることで、

  • 特定の具体的な型を返す関数に依存

  • アクセスする予定のない内部オブジェクトにアクセスする危険

を回避できるらしい👀さらに

API の開発者が、API を使用するコードの依存関係を壊すことを心配することなく、基礎となる実装を変更できる (別のプロトコルに準拠した型を返すなど) ことができるという利点

があるらしい。まあ、API自体をよく分からずに使って上手くいかない時に、API自体をカスタマイズしたりせずに、安全に機能拡張なんかができるよってことみたいだね

うーむ。かなり強力ちゃんだねえ🤔
オイラ的には、そんな時には、
次章以降で出てくる構造体とか拡張機能なんかを使えばいい気もするが、、、
APIちゃん自体を触るとか拡張するって発想がよく分からない💦
危険過ぎる気がする

ま、後は

不透明な戻り値の型を使用するときに誤った仮定が行われた場合に何が起こるか

って解説で、

let intOne = doubleFunc1(value: 10)
let stringOne = doubleFunc2(value: "Hello")
if (intOne == stringOne) {
    print("They match")
}

てな感じで、コードを比較すると、

Swift コンパイラーはこの隠された情報にアクセスできるから

実際に、ここのコードをはめ込むと

てな感じのエラーが実行前の段階で発生してるね👀

ここが

コードを書いた瞬間に安全にエラーを検知してくれる
てゆーSwiftが静的プログラミング言語である最大の利点なんだよね〜〜〜

今回(第10章)のまとめコード

/**:-------------------
 Essentials 第10章 Swiftオブジェクト志向プログラミング
 ---------------------*/
class ProfileAccount10_1 {
    //ストアドプロパティ
    var m_KakuAge: Int = 43
    var m_KakuWeight: Float = 68.9//キログラム
    var m_KakuHeight: Float = 1.7//メートル
    var himanFlg: Bool = false
    var notFatFlg: Bool = true
    //コンピューテッドプロパティ:BMI計算
    var calcBMI: Float {
        get {
            return m_KakuWeight / (m_KakuHeight * m_KakuHeight)
        }
        set(resultBMI) {
            if resultBMI > 25 {
                himanFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //コンピューテッドプロパティ:肥満未満の体重上限
    var nonFatMyWeight: Float {
        get {
            return 25 * (m_KakuHeight * m_KakuHeight)
        }
        set(weightFatResult) {
            if weightFatResult - m_KakuWeight > 0 {
                notFatFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //イニシャライザ
    init(m_KakuAge: Int, m_KakuWeight: Float) {
        self.m_KakuAge = m_KakuAge
        self.m_KakuWeight = m_KakuWeight
    }
    //デイニシャライザ
    deinit{
        print("deinit")
    }
    //タイプメソッド
    func msgMyProfile(){
        print("オイラは\(m_KakuAge)のおっさんだぞ")
        print("オイラの体重は\(m_KakuWeight)だよ。運動せんと、、、💦")
    }
    //インスタンスメソッド
    class func getMinimunWeight() -> Float {
        return 49.9
    }
}
var myProfile: ProfileAccount10_1 = ProfileAccount10_1(m_KakuAge: 42, m_KakuWeight: 59.0)
//初期化したインスタンスからプロパティ呼び出し
var myAge = myProfile.m_KakuAge
print(myAge)
//違う年齢を代入して
myProfile.m_KakuAge = 52
//タイプメソッド呼び出し
myProfile.msgMyProfile()
//インスタンスメソッド呼び出し〜〜
var minWeight = ProfileAccount10_1.getMinimunWeight()
//自分の体重を48.9に代入
var myWeight = myProfile.m_KakuWeight
myWeight = 48.9
//比較して〜〜
if myWeight > minWeight {
    print("良い感じ")
} else {
    print("痩せ過ぎじゃない?")
}
//BMI計算結果を呼び出す
var myBMI = myProfile.calcBMI
print(myBMI)
//肥満かどうかの判定
var myFat = myProfile.himanFlg
if myFat == false {
    print("肥満じゃないです")
} else {
    print("肥満に注意")
}
//肥満未満になる体重上限値を取得
var myNotFatMax = myProfile.nonFatMyWeight
print(myNotFatMax)
var notFatFlg = myProfile.notFatFlg
if notFatFlg == true {
    print("まだ太れます")
} else {
    print("運動しましょう")
}

//Lazy以降
var myAge10_2 = 43
//普通のクラスだと、
class MyAge10_2{
    var myTitle: String
    init(myTitle: String) {
        self.myTitle = myTitle
    }
}
//ファイルの属性を遅延評価によって取得
class FileAttribute10_3{
    let fileName: String
    //遅延評価で値を決定
    lazy var size: Int = self.getFileSize()
    init(file: String) {
        fileName = file
    }
    func getFileSize() -> Int{
        //構造体の初期化
        var buffer = stat()
        //stat呼び出し
        stat(fileName, &buffer)
        //動作確認のための印字
        print("[getFileSize]")
        //得られた値をInt型に変換
        return Int(buffer.st_size)
    }
}
//実行
let d10_3 = FileAttribute10_3(file: "text.txt")
print(d10_3.fileName)
print(d10_3.size)
print(d10_3.size)

//ファイルの属性を遅延評価によって取得クロージャ版
class FileAttribute10_4{
    let fileName: String
    //クロージャ版
    lazy var size: Int = {
        var buffer = stat()
        stat(self.fileName, &buffer)
        print("[getFileSize]")
        return Int(buffer.st_size)
    }() //ここの()は必須
    init(file: String){
        fileName = file
    }
}
//実行
let d10_4 = FileAttribute10_4(file: "text.txt")
print(d10_4.fileName)
print(d10_4.size)
print(d10_4.size)

class MyCalc10_5{
    var myNum = 10
    func minus1() {
        self.myNum -= 1
    }
}

class MyCalc10_6 {
    var myNum = 10
    func minus1(myNum: Int) {
        print(myNum)
        print(self.myNum)
    }
}

protocol M_KakuProfile10_7{
    var name: String{ get }
    func msgBuild() -> String
}

class MKakuProfile10_7: M_KakuProfile10_7{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgBuild() -> String {
        "オイラの名前は、" + name
    }
    //不透明な戻り値 Opaque return type
    //Equatableプロトコルを使うみたいだね
    func tripleCalc(value: Int,str: String) -> some Equatable{
        value * 3
        return str + "様"
    }
}
//以下は不透明な戻り値の比較例エラーになるからコメントアウト
/*
func doubleFunc1(value: Int) -> some Equatable {
    value * 2
}
func doubleFunc2(value: String) -> some Equatable {
    value + value
}
let intOne = doubleFunc1(value: 10)
let stringOne = doubleFunc2(value: "Hello")
if (intOne == stringOne) {
    print("They match")
}
 */

今回から中々一回で理解しようってするのは骨が折れるし、自分で色々動かしながらじゃないと分からないことも多いと思うので。

いろんなクラスとかプロトコルを作って動かしてみてね〜〜〜!
(実際の、SwiftUIでの組み込みのところでここまで使うのか?
(例:lazyなど)は別として。。。)

ま、オイラなら、

へえ〜〜〜こんなコードもあるんだくらいで、さらっと動かして触れて、
実践の方に行って分からなくなったら読み返す

って感じで進めるかな🧐

特に今回の記事で書いてるモノは、

下手にコードだけで理解しようとこだわると底なし沼にハマるくらい強力で奥が深いものばかりなんでね(前回の概要部分で書いてるとおり💦👀)なので前回の最後に、

ちょっと毛色が変わってくるからって書いた次第なんだけど。

普段気にしないし、何年か経ってるからもう忘れちゃってるけど、

にたしか公開されてるプロトコルだけでも山のように一覧が載ってた記憶があるから、そこまで完全に理解したいとか思ってるならやればいい。

ただし、実際にそれを丸暗記したからって実際にアプリが作れるわけじゃないから、

英語辞書を丸暗記して、英語を忘れる英語圏の人

くらい意味ないとは思うけどね。

Apple公式

さてと、次回は、

今回の記事でちょろっと言葉だけ触れた、

拡張機能:エクステンション と サブクラスをやってく〜〜〜!

やっぱり、英語の学びにならないし、サンプルコードまで載ってると、自分で手で打たないと意味ないから、

次回からも、じっくりゆっくりやろ。

WEBはサンプルコードも載ってはいるが、

手で実際にコードを打つ→動かす→理解する

のサイクルなく、分かった気にだけなれるから恐ろしい😱

記事公開後

でやった手順でいつも通り〜〜〜

コード
ポイント
参考リンクが今回は2個のうち1個目〜〜〜
2個目〜〜〜

コード(各ファイルごとに前回分も含めて一気に〜〜〜)

Essentials10.swift(今回新規追加)

import SwiftUI
import WebKit

struct Essentials10: View {
    var body: some View {
        VStack{
            TabView {
                Essentials10Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials10Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials10WEB1()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
                Essentials10WEB2()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
 #Preview  {
    Essentials10()
}

struct Essentials10Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials10)
        }
    }
}
 #Preview  {
    Essentials10Code()
}

struct Essentials10Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials10)
        }
    }
}
 #Preview  {
    Essentials10Points()
}

struct Essentials10WebView1: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}

struct Essentials10WEB1: View {
    private var url:URL = URL(string: urlEssentials10_1)!
    var body: some View {
        Essentials10WebView1(searchURL: url)
    }
}
 #Preview  {
    Essentials10WEB1()
}

struct Essentials10WebView2: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}

struct Essentials10WEB2: View {
    private var url:URL = URL(string: urlEssentials10_2)!
    var body: some View {
        Essentials10WebView2(searchURL: url)
    }
}
 #Preview  {
    Essentials10WEB2()
}

iOSApp17DevelopmentEssentialsCh10(今回新規追加)

import SwiftUI

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh10: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh8
}

//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh10 {
    case Sec1
}

//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh10: [ListiOSApp17DevelopmentEssentialsCh10] = [
    ListiOSApp17DevelopmentEssentialsCh10(id: 1, title: "第1節", view: .Sec1)
]

struct iOSApp17DevelopmentEssentialsCh10: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh10) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh10(dataiOSApp17DevelopmentEssentialsCh10: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("10章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh10(dataiOSApp17DevelopmentEssentialsCh10: ListiOSApp17DevelopmentEssentialsCh10) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh10.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials10()) {
                Text(dataiOSApp17DevelopmentEssentialsCh10.title)
            })
        }
    }
}
 #Preview  {
    iOSApp17DevelopmentEssentialsCh10()
}

iOSApp17DevelopmentEssentials.swift

import SwiftUI

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}

//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり13で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
}

//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: "第1章", view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: "第2章", view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: "第3章", view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: "第4章", view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: "第5章", view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: "第6章", view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: "第7章", view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: "第8章", view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: "第9章", view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: "第10章", view: .Ch10)
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}
 #Preview  {
    iOSApp17DevelopmentEssentials()
}

CodeManageFile.swift

let codeEssentials10 = """
/**:-------------------
 Essentials 第10章 Swiftオブジェクト志向プログラミング
 ---------------------*/
class ProfileAccount10_1 {
    //ストアドプロパティ
    var m_KakuAge: Int = 43
    var m_KakuWeight: Float = 68.9//キログラム
    var m_KakuHeight: Float = 1.7//メートル
    var himanFlg: Bool = false
    var notFatFlg: Bool = true
    //コンピューテッドプロパティ:BMI計算
    var calcBMI: Float {
        get {
            return m_KakuWeight / (m_KakuHeight * m_KakuHeight)
        }
        set(resultBMI) {
            if resultBMI > 25 {
                himanFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //コンピューテッドプロパティ:肥満未満の体重上限
    var nonFatMyWeight: Float {
        get {
            return 25 * (m_KakuHeight * m_KakuHeight)
        }
        set(weightFatResult) {
            if weightFatResult - m_KakuWeight > 0 {
                notFatFlg = true
            } else {
                himanFlg = false
            }
        }
    }
    //イニシャライザ
    init(m_KakuAge: Int, m_KakuWeight: Float) {
        self.m_KakuAge = m_KakuAge
        self.m_KakuWeight = m_KakuWeight
    }
    //デイニシャライザ
    deinit{
        print("deinit")
    }
    //タイプメソッド
    func msgMyProfile(){
        print("オイラは\\(m_KakuAge)のおっさんだぞ")
        print("オイラの体重は\\(m_KakuWeight)だよ。運動せんと、、、💦")
    }
    //インスタンスメソッド
    class func getMinimunWeight() -> Float {
        return 49.9
    }
}
var myProfile: ProfileAccount10_1 = ProfileAccount10_1(m_KakuAge: 42, m_KakuWeight: 59.0)
//初期化したインスタンスからプロパティ呼び出し
var myAge = myProfile.m_KakuAge
print(myAge)
//違う年齢を代入して
myProfile.m_KakuAge = 52
//タイプメソッド呼び出し
myProfile.msgMyProfile()
//インスタンスメソッド呼び出し〜〜
var minWeight = ProfileAccount10_1.getMinimunWeight()
//自分の体重を48.9に代入
var myWeight = myProfile.m_KakuWeight
myWeight = 48.9
//比較して〜〜
if myWeight > minWeight {
    print("良い感じ")
} else {
    print("痩せ過ぎじゃない?")
}
//BMI計算結果を呼び出す
var myBMI = myProfile.calcBMI
print(myBMI)
//肥満かどうかの判定
var myFat = myProfile.himanFlg
if myFat == false {
    print("肥満じゃないです")
} else {
    print("肥満に注意")
}
//肥満未満になる体重上限値を取得
var myNotFatMax = myProfile.nonFatMyWeight
print(myNotFatMax)
var notFatFlg = myProfile.notFatFlg
if notFatFlg == true {
    print("まだ太れます")
} else {
    print("運動しましょう")
}

//Lazy以降
var myAge10_2 = 43
//普通のクラスだと、
class MyAge10_2{
    var myTitle: String
    init(myTitle: String) {
        self.myTitle = myTitle
    }
}
//ファイルの属性を遅延評価によって取得
class FileAttribute10_3{
    let fileName: String
    //遅延評価で値を決定
    lazy var size: Int = self.getFileSize()
    init(file: String) {
        fileName = file
    }
    func getFileSize() -> Int{
        //構造体の初期化
        var buffer = stat()
        //stat呼び出し
        stat(fileName, &buffer)
        //動作確認のための印字
        print("[getFileSize]")
        //得られた値をInt型に変換
        return Int(buffer.st_size)
    }
}
//実行
let d10_3 = FileAttribute10_3(file: "text.txt")
print(d10_3.fileName)
print(d10_3.size)
print(d10_3.size)

//ファイルの属性を遅延評価によって取得クロージャ版
class FileAttribute10_4{
    let fileName: String
    //クロージャ版
    lazy var size: Int = {
        var buffer = stat()
        stat(self.fileName, &buffer)
        print("[getFileSize]")
        return Int(buffer.st_size)
    }() //ここの()は必須
    init(file: String){
        fileName = file
    }
}
//実行
let d10_4 = FileAttribute10_4(file: "text.txt")
print(d10_4.fileName)
print(d10_4.size)
print(d10_4.size)

class MyCalc10_5{
    var myNum = 10
    func minus1() {
        self.myNum -= 1
    }
}

class MyCalc10_6 {
    var myNum = 10
    func minus1(myNum: Int) {
        print(myNum)
        print(self.myNum)
    }
}

protocol M_KakuProfile10_7{
    var name: String{ get }
    func msgBuild() -> String
}

class MKakuProfile10_7: M_KakuProfile10_7{
    var name: String
    init(name: String) {
        self.name = name
    }
    func msgBuild() -> String {
        "オイラの名前は、" + name
    }
    //不透明な戻り値 Opaque return type
    //Equatableプロトコルを使うみたいだね
    func tripleCalc(value: Int,str: String) -> some Equatable{
        value * 3
        return str + ""
    }
}
//以下は不透明な戻り値の比較例エラーになるからコメントアウト
/*
func doubleFunc1(value: Int) -> some Equatable {
    value * 2
}
func doubleFunc2(value: String) -> some Equatable {
    value + value
}
let intOne = doubleFunc1(value: 10)
let stringOne = doubleFunc2(value: "Hello")
if (intOne == stringOne) {
    print("They match")
}
 */
"""

PointManageFile.swift

let pointEssentials10 = """
クリエイターの目線から(今回の一番大事なポイント)
オイラはいっつも、
コロナ禍でテレワークとかフルリモートなんかをやってると、
運動したくても出来ないことが多くなってたのに、
運動しましょうって感じで、今の体重が肥満かどうかを教えてくれる体重計とかはあるのに

何キロになったら肥満か
👉あと何キロまで太っても肥満じゃないか

を教えてくれる体重計がないのが不思議だったんだよね〜〜〜👀
まあ、去年、

まったり経営学|M_Kaku堂|note
日々読んだ経営学書の感想をまったり書いてるだけ💦
note.com
なんかで散々、書いたとおり、

健康維持には運動するのが当たり前ってレジティマシーが強い
👉脳筋マッチョな医学とかスポーツ界の常識

が原因なんだろうけど、

災害や事故で骨折してしばらく運動ができない

世界的な疫病で運動したくても、ジムが閉まっていて運動できない

なんて状況をオイラたちはここ数年で経験して、

スタンフォードの自分を変える教室 スタンフォード シリーズ
www.amazon.co.jp
814円
(2024年01月07日 16:48時点 詳しくはこちら)
Amazon.co.jpで購入する
でも書いてるハロー効果やドーパミン効果を悪用した、

不安を煽って消費に繋げる=悪意ある経済
👉ただお金が儲かれば良いのか?

から、

人の不安を煽らず必要な消費に繋げる=倫理ある経済
👉いかにお金を稼ぎ循環型の経済に社会を変えていくか

に繋がると思うしね、、、。この

略して、Essentials
のSyntax以降で出てくるであろうSwiftUIのフレームワークなんかで身長をメートル単位で入力だけしてもらえれば、

痩せ型は何キロ以下、肥満は何キロ以上

って判定や適正体重なんかも、

体重計なしにスマホひとつで計算できる

から、さっき書いたような事情で、運動がしたくても中々できないって人に、

無理に不安感を煽ったり、罪悪感を持って運動する

なんてことも減るだろうし、今、世界的な社会問題になりつつある
GLP-1受容体作動薬のダイエット目的の消費抑止にも繋がると思うしね。。。。👀そのきっかけは、ただ単に、
体重計で使ってるBMIの計算方法を逆転させただけ
👉よくいう発想の転換
(ゆーばかりでやれてない経営者ばかりだけど)
なんだけどね。ま、だからまだ運動しなくても大丈夫とか、健康を維持できるなんて豪語するつもりもないし、スマホアプリにそこまでの力なんてもちろんないけど、
テレワークや災害・事故、美容なんかで強迫観念を持ってる人の不安を少し減らすことができるかもしれない
👇
技術を正しく使えば、そんなことも出来るし、
最新の技術にはそんな力を秘めてる
ってことなんだけどね👀世の中に、数百万くらいあるスマホアプリを全部見てるわけでもないから、すでに存在してるかもしれないし、別にそれをオイラが作らないといけないとかこんなアイデアを独り占めする気もないから今ここで書いてるけどね。

どんな技術もツールも使い方ひとつで、
世界中のすべての人を幸せに豊かにすることも出来れば、
ごく一部の人間だけで、利益も富も独占出来るように悪用することも出来る

ってことで、、、。

去年で、
書きたかった記事はSwiftUI以外はあらかた全て書き終えてる(1年間で580記事くらいかな)から、今年からあんまり趣味のSwiftや古典、音楽、英語以外は書く気もないけどね。

オイラのこんな話なんて一個人のただの私見とか持論に過ぎないから、プロスペクト理論で、いくらでも

そうなるとは限らない

で一蹴してくれていいけど、年末年始も新聞を隅々まで読んでて今年は、

日本が色々な意味で完全に再生(ReStart ,Reset,Reborn)が始まる年

で、自分の所属する会社とか、研究室とか地域といった範囲でしか物事を見れなかった人たちが、

自分のいる産業、学会、社会といった範囲に行動範囲自体を広げないと淘汰されるしだろう。本格的に淘汰が始まる最初の年だろうな

って見てるんだよね👀実際、

すでに4月から運輸や医学会なんかで時間外労働の規制が始まることで去年から政治や経済が大好きな人たちは右往左往してるのが象徴的な出来事だしね。

遅延格納型プロパティ(lazy stored property)とは
プロパティの値を遅延評価で決定するクラス、または構造体のインスタンスプロパティのうち、格納型プロパティには、初期化の際には値を決めず、必要とされた時に初めて値を決定するという挙動を指定できる。
→プロパティとして定義した値を全て使うばかりではないし、計算コストを考えれば、必要な時まで決めないのは賢明
*タイププロパティは元々そんな値の決定の仕方

格納型プロパティの先頭にlazyを置く。
変数の型は明記しておいた方が安全。
定義できるのは、クラスか構造体の内部のみ。
プロパティの遅延評価=インスタンス生成後、インスタンス自体の値を変更する
👉定数ではなく、変数で宣言が必要。

・構造体の場合
そのプロパティの値を参照することによって遅延評価を引き起こす可能性のあるメソッドにはmutatingを付ける必要あり。

・遅延格納型プロパティには、プロパティオブザーバを設定できない。
・複数のスレッドが同時にプロパティにアクセスする状況で、初期化が一度だけ行われる保証はない。

しっかり理解せずに安易に、コードを修正すると、

まさに、

沼

にハマってしまうし、
実は複雑なコードでどうしても遅延バインディングをしないといけない時に、lazyを使うくらいな感覚で、ここは済ませて次に進んだ方がいい。

だから


の本文なんかで、

独学プログラミングのコツは紹介してるんだけどね💦👀

1周目でわからないことがあるくらい当たり前だし、
何かエラーがある度に全て一度で理解しようなんてしていたら、

他の理解できることを捨ててるようなもの
👉まさに、

木を見て森を見ず

になるからね。

ちなみにこの本で、
lazy

ってキーワードで本全体を検索してみたんだけど、

LazyGridLayout

なんかでヒットするだけで、Syntax以外でもほぼサンプルコードは出てこないみたいだから、

lazy =遅延バインディングで使う

だけを理解しておけばここでは十分。遅延バインディングすら、

複雑なURLSessionなんかでしか使うことがないからね。
👉いっぺんで理解しようとすることがそもそも挫折の原因
(オイラは記事を書いてる手前、2時間くらい調べたりはしてる程度。
そもそも1回で全てを理解してるとか、まだ、実践のコードを書いてもないのに、この本のコードを全て1回で暗記や理解するエンジニアなんていないし、それが出来たからって何の意味があるのかを逆に知りたいけどね👀)

目的は、

iOS17を学び直しがてら理解しつつ、

自分の作りたいアプリが最低限作れるようになればいいだけ

だからね。なので、以前の記事でも書いたけど、

自分が学ぶ目的は何かをしっかり見定めた上で学ぶことが大事

って書いた次第。ま、それでも

それくらいは理解するのがプロのエンジニアだ

これくらい1回で理解できないと優秀なエンジニアになれない

みたいな勝手なエンジニア像を思い描いて邁進したいなら、勝手にどうぞ。
そんなもん現場を知らない素人のただの思い込みだし、

ほぼ実務でも最初のうちは使わないモノまで全て一回で理解しようなんてやってる人も過去に見たことあるけど、ほぼほぼ挫折してる人ばかりだからね。
👉自分から挫折しにいくって愚か者としか言いようがないでしょ👀💦
"""

URLManageFile.swift

import Foundation

let urlSwiftUIMM1_1 = "https://note.com/m_kakudo/m/m681b1be679c5"
let urlDevEssentials1_1 = "https://sites.google.com/view/masaboct3/%E3%83%9B%E3%83%BC%E3%83%A0/"
let urlEssentials1 = "https://note.com/m_kakudo/n/n28c4d1b491e7"
let urlEssentials2 = "https://note.com/m_kakudo/n/n23f98cc63aee"
let urlEssentials3 = "https://note.com/m_kakudo/n/n5653a10d2cc7"
let urlEssentials4 = "https://note.com/m_kakudo/n/n5db03cd31141"
let urlEssentials5 = "https://note.com/m_kakudo/n/n87ebb286c1b5"
let urlEssentials6 = "https://note.com/m_kakudo/n/n8d342f5d8431"
let urlEssentials7 = "https://note.com/m_kakudo/n/nd42cef1bb81f"
let urlEssentials8 = "https://note.com/m_kakudo/n/ne914360177ea"
let urlEssentials9_1 = "https://note.com/m_kakudo/n/n0fb056cb4495"
let urlEssentials9_2 = "https://note.com/m_kakudo/n/nafe78303bdff"
let urlEssentials10_1 = "https://note.com/m_kakudo/n/n3eb8aed6473b"
let urlEssentials10_2 = "https://note.com/m_kakudo/n/nd12ab0e5dbf4"

以上。

この記事が気に入ったらサポートをしてみませんか?