2023年6月2日金曜日

NSObjectとは

  •  Objective-Cの基本的なルートクラスで、Swiftでも使用される。
  • 他のクラスが継承することで、多くのメソッドやプロパティを利用可能。
    • メモリの管理
    • オブジェクトの等価性の比較
    • クラス情報の取得
    • メソッドの動的な呼び出し
  • ObjectiveーCのプロトコルに準拠しており、相互運用性が向上する。
  • ObjectiveーC特定の機能やAPIを利用する必要がある場合に有効。

AnyObjectとは

  •  Swiftの型システムにおける、プロトコルの一つ。
  • 参照型であるクラスのインスタンスを表すための型。
    • 参照型(class):参照を介して共有
    • 値型(struct, enum):値そのものをコピーして扱う。
  • クラスのインスタンスをAnyObject型の変数やプロパティに代入できる。
  • どのクラスのインスタンスでも受け入れられる。
  • 値型には使用できない。
  • AnyObject型の変数やプロパティには任意のクラスのインスタンスを代入できるが、メソッドの呼び出しなど、クラスのインスタンス特有の操作はできない。

weak 修飾子について

プロパティにweak修飾子をつける理由
  • 循環参照(retain cycle)を避けるため
    • 循環参照とは?
      • メモリリークの一種でオブジェクト間の相互参照によって発生。
        • メモリリーク(Memory Leak)とは?
          • プログラムやアプリにおけるメモリの不適切な使用状態。
          • 不要なメモリの確保や解放漏れにより、メモリ使用量が増加し続ける状態。
      • 2つ以上のオブジェクトがお互いに強参照(strong reference)を持ち、相互に解放されなくなる状態を指す。
        • 強参照とは?
          • オブジェクトが他のオブジェクトを所有することを意味する。
          • 参照カウントが増加し、オブジェクトが解放されない限り、メモリ上に保持され続ける。
          • 強参照がデフォルト。
      • 循環参照が発生すると?
        • オブジェクトが互いに解放されなくなり、メモリの解放が行われない。
        • アプリのメモリ使用量が増加し続け、最終的にメモリリークが発生。
        • メモリリークが継続すると?
          • アプリのパフォーマンス低下やクラッシュの原因となる。
      • 循環参照は相互に強参照を持つ場合に発生する。
    • 弱参照(weak reference)とは?
      • 参照カウントを増加させず、参照先オブジェクトが解放されると、自動的にnilになり、循環参照を解消するのに役立つ。

2023年3月26日日曜日

[SwiftUI]TextFieldで文字列ではなく、数値やnilを扱いたい(挙動の違いも検討)

はじめに

 以前SwiftUI学習のために作成したアプリ(リンク)において、使用者様から、「TextFieldに金額を入力して、その金額のお皿を追加するButtonをタップした際にTextFiledの入力値をクリアできたら良いのでは」というご指摘をいただきました!(ありがとうございます!)

 そこで対応していた際に、うん?簡単そうでうまくいかないぞ?っていう点があり、調べても調べても解決できず苦労した点をここに記します!多分不具合か仕様かと思っています!

詳細わかる方いましたらコメントください!一旦個人的な見解を述べてます!


現在の開発環境

・PC:MacBook Air M1
・Xcode:Version 14.2
・Swift :Version 5.7.2

文字列ではなく、数値を入力値として扱うには?

 TextFieldで扱う値を数値だけにするには、引数textではなく、value及びformat(formatter)を用います。
 textはString型であり、そのままでは数値やnilを許容するオプショナル型を扱えません。format: .numberを指定することで数値にフォーマットすることができます。
 入力するキーボードを指定するには.keyboardType(.numberPad)モディファイアを付与します。

ここで問題が・・・

 valueとformatterを用いていたので、Buttonタップ時にvalueにバインディングしている変数に空文字列""を代入すれば解決・・・とはいきません。
変数には数値を扱うためにInt型にしています。しかし"0"を代入すると"0"が残るので、オプショナル型を用いる必要がありますね。"0"でも良いのですが、指でTextFieldをタップして数値を入力する際に、微妙に邪魔してくれるので、消しましょう。
Buttonタップ時の処理に、@Stateを付与してTextFieldにバインディングしている変数にnilを代入する処理を追記します。
ここまでは何ら問題ありません。

 ここからが問題でした。シミュレータにて、挙動を確認したら、あら不思議。
何ということでしょう・・・TextFieldの値が、消えないではありませんか。
 しかしながら、printで出力して値を確認すると、当該変数の値は変更されています。・・・であれば、Viewが更新されていない可能性があると考え、調べても調べても情報はありません。@StateはViewとリンクしている、これはSwiftUIにおける最大のメリットと考えていましたが・・・こうも裏切られるとは・・・

解決のために

 プロジェクト上では他のコードが影響あるのでは?という懸念を振り払うため、新規プロジェクトを作成し、余計なコードは書きません。
 TextFieldとButtonだけ配置し、さあ実験。結果は・・・やはり変わりません。
そこで、TextFieldをもう一つ増やし、そちらは引数textで挙動を確認しました。
すると驚くことが判明しました。
 textにバインディングしている変数の値に””を代入するパターンはしっかりViewに反映されるのです。そう、valueだけが、nilも数値も、変数に代入した値がViewに反映されていない、ということがわかりました。もちろん変数の値は変更されています。(printで確認ずみ)
 しかし、ある条件下ではvalueの方もしっかりとViewが更新されたのです。これはさまざまな実験をする中での偶然の気付きでした。

原因は・・・?

 その条件とは、value側のTextFieldに値を入力した後、text側のTextFieldを選択して、Buttonをタップすることです。要は選択(フォーカス)状態が解除されている場合のみ、TextFieldの値が変更されたのです!text側は選択(フォーカス)状態関係なくViewが更新されるのに・・・。

 詳細な原因は判明しておりませんが、仕様としてこのような挙動がある、ということがわかりました。なので、同じように沼にハマってしまった方にも情報を共有できたらと思い、記事にしました。私自身、まだモヤモヤは残りますが、一旦挙動の違いということがわかり、悩み続けることがなくなり一安心です。
 共感や解決策等ありましたら、お気軽にコメントお願い致します!

以上!

追記

 ちなみに、引数formatではなく、formatterを用いた場合、TextFieldに値を入力しても、リターンキーを押されないと、変数に値が反映されず選択(フォーカス)状態が解除された時点で入力した値が消えます。numberPadにはエンターキーがないため、強制的に解決するしかなくなります。こちらの記事にこの挙動については記載がありました。これにもかなり悩まされました・・・

 あと、流行りのchatGPTに聞いたら、オプショナル型を扱うとViewが更新されないと教えられましたが、大嘘でした笑。

2023年1月8日日曜日

Firestoreでのデータの保存について2

 昨日に引き続き、今回は、Firestoreにデータの保存(書き込み)ができるか、確認を行います。

Firestoreへの保存確認

アプリ名.swiftファイルを以下のように編集する。

import SwiftUI

import FirebaseCore

import Firebase

import FirebaseFirestore


class AppDelegate: UIResponder, UIApplicationDelegate {

    

    func application(_ application: UIApplication,

                     didFinishLaunchingWithOptions launchOptions:                   [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        FirebaseApp.configure()

        Firestore.firestore().collection("users").document("Message").setData([

            "UserMessage": "message",

            "Date": "messageDate",

            "UserId": "messageId",

        ], merge: false) { err in

            if let err = err {

                print("Error writing document: \(err)")

            } else {

                print("Document succesfully writen!")

            }

        }

        return true

    }

}


アプリをビルドした際にデータが保存されているか、Cloud Firestoreで確認する。

usersというコレクションの中に、
Messageというドキュメント、さらにその中に、
以下のデータが格納されていることが確認できます。
  • Date"messageDate"、
  • UserId "messageId"、
  • UserMessage "message"
これで親、子、孫のように階層状にデータが保存できることがわかりました!

 


2個目の個人開発アプリ、リリースしました!

この年末年始で個人開発アプリ、2個目を無事にリリースしました!

開発環境

  • Xcode 14.2
  • SwiftUI
  • MacBook Air M1 メモリ8G

開発期間

 約6日間、合計40時間程度
 (リジェクト対応含む)

アプリコンセプト

 アプリ起動後簡単に、一定時間毎に通知するタイマーを設定できる

アプリ開発背

 つい最近、こたつを朝からつけっぱなしで外出して夕方に気がつき、妻に叱られたので「スマホのアラーム機能をわざわざ設定するのも面倒だよなぁ・・・」と思い、「じゃあアプリ起動するだけで通知できるようなアプリを作ればいいじゃん!」ってことで開発しました!
他の学習で行き詰まっており、息抜きも兼ねてです笑

想定するユーザー

 こたつやストーブを消し忘れやすい人たち(私)

実装した機能

  • UserNotificationを用いた通知機能(アプリ起動中でもバックグラウンドでも通知)
  • UserDefaulsを用いた内部データ保存機能
  • Timer.scheduledTimerを用いたカウントダウン機能
  • NavigationStackを用いた画面遷移(かなり特殊に感じました・・・)
  • 通知が許可されていない場合に表示する、アラート機能(設定画面への遷移含む)
  • GoogleAdmobを用いたインタースティシャル(全画面)広告表示機能

アプリ画像





アプリURL

 もしよかったら、ダウンロードしてみてください!

このアプリの今後について

 特にありませんが、機能改修希望や不具合ありましたら、ご連絡いただければ対応考てます!

2022年10月25日火曜日

Firestoreでのデータの保存について1

前回制作したアプリでは、Realmを用いてデータの保存処理を行なっていました。

しかし、端末内に保存されるため、アプリを削除すると、データも共に消えてしまいます。

そのため、今回はFirebaseのCloud Firestoreというものを使って、データの保存処理について実装します。そこで学習したことを簡単にまとめていきます。

概要

  • モバイル開発、ウェブ、サーバー開発に対応したデータベース
  • 階層データ構造に対応
  • リアルタイムアップデート
  • オフラインサポート(オフラインでもキャッシュから読み書き可能)

開発環境:Xcode (Ver.13.4.1)、SwiftUI

実装について(Firestore自体の設定は省略。基本的に手順通りに進める)

  1. Firebaseにアクセスし、プロジェクトを作成。
  2. iOSアプリを選択し、アプリの登録を行う。
  3. バンドルIDはXcodeにて確認できます。
    (アプリプロジェクトファイル選択 >TARGETSのプロジェクト選択
     >General >Bundle Identifier)
  4. 設定ファイルをダウンロードし、Xcodeにドラックアンドドロップで追加する。
    (Copy items if neededにチェックを入れること)
  5. Firebase SDKの追加
    CocoaPodsやSPM(Swift Package Manager)にてインストールします。
    私は、SPMにて導入しました。導入方法は以下の通りです。
     XcodeのFile>Add Packagesを選択し、右上の検索窓に、
     「https://github.com/firebase/firebase-ios-sdk.git」を入力し、必要なライブラリを導入する。
    ただしここが一番の躓きポイントでした・・・
    様々なサイトや書籍を参考にしたものの、どれを導入すべきか、明記しているものがなく、記載通りではエラーが出るなどして、かなり導入に時間を要しました。
    肝心のところを・・・覚えてません。思い出し次第追記します。
  6. 初期化コードの追加
    SwiftUIの場合の導入については、公式ドキュメントに記載がないので、他の方の記事を参考に記述しました。というか、Firebase公式ドキュメント、何事もざっくりしか書かれておらず、これだけでは初学者には実装困難です。
    AppDelegate.swiftファイルはないため、プロジェクト名.swiftに以下を追記します。
    ・import Firebase
    ・class AppDelegate内のfunc application(~)内に FirebaseApp.configure()
  7. 次にまたウェブに戻り、Cloud Firestore(Firestore Database)を選択し、ルールの設定などを行う。
     allow read, write: if true;  tureにすることで、アクセス可能になります。
  8. ここからは、データの書き込みが行われているかの確認手順ですが、続きは後日・・・?

参考書籍:つくって学ぼiOSアプリ開発

flutterについて、学んだことなど

Swiftについて学んできたため、つい比較してしまいがちです。 SwiftUIと比較して、書いてみました。 似ているようで似ていない flutterには、SwiftUIにおける「View」という概念が、「Widget」と呼ばれるものになる SwiftUIのようにWidgetの中に...