20211129のiOSに関する記事は5件です。

UIViewControllerの一部だけをSwiftUIで構築した話

前書き ACCESS Advent Calender 今年の1日目は@tonionagauzziです。 1月に娘が生まれて、コロナ禍での育児1年生でしたが、会社や周囲の手厚いサポートによって無事に過ごせました。この場を借りて、1年間お世話になった皆さんにお礼を申し上げます。 そしてようやく念願叶い、両家に顔見せに行った帰りの飛行機で、明後日投稿日じゃん!って気づいてスマホで記事書いてます。便利な時代になりましたね! やったこと UIKitで書かれた歴史の長いiOSアプリがあります。最近、その Deployment Target が13.0に上がったので、UIViewControllerの一部だけにSwiftUIを導入しました。 また、SwiftUIで、URLは青下線表示で押したらSafariが開くようにしました。 このような Key-Delimiter-Value 形式のViewです。 SwiftUIを導入した狙いは、 要素数(縦の行数)が不定なので、SwiftUIのForEachで手軽に可変にしたい UIKitと比べて20%以下のコード量で同じ内容を記述できるので、開発効率を上げたい 単純なViewでデザイン制約がないので、SwiftUI導入のきっかけ作りをしたい でした。 上に貼ったスクショはプレビューなので、実際は上下に従来のUIKitのパーツが並んでいる想定です。 説明 1. UIViewController上の一部だけをSwiftUIにする まず、URLは置いといて、UIViewControllerにSwiftUIを埋め込む部分のコードを載せます。 ポイントは3つです。 UIHostingControllerにSwiftUI Viewを紐付け、UIKitのViewControllerにaddChildする SwiftUI Viewを空のUIViewにaddSubViewする 2の親子が密接するようConstraintを設定する こう書くと面倒くさそうですがコード量は大したことないです。 Item.swift struct Item: Hashable { var name: String var value: String } ExtraView.siwft import SwiftUI let ITEM_LIST_FOR_PREVIEW = [ Item(name: "名前", value: "tonionagauzzi"), Item(name: "SNS", value: "https://twitter.com/tonionagauzzi"), Item(name: "Motto", value: "You can decide you're happy or not.") ] struct ExtraView: View { var itemList: [Item] = [] @ViewBuilder var body: some View { GeometryReader { geometry in VStack { ForEach(itemList, id: \.self) { item in HStack(alignment: .top) { Text(item.name) .frame(maxWidth: geometry.size.width * 0.12, alignment: .leading) Text(":") Text(item.value) .frame(maxWidth: .infinity, alignment: .leading) }.padding(.bottom, 1) } } .padding(.horizontal, 10) } } } struct ExtraView_Previews: PreviewProvider { static var previews: some View { ExtraView( itemList: ITEM_LIST_FOR_PREVIEW ) } } MyViewController.swift import SwiftUI class MyViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 省略 let extraViewController: UIHostingController<ExtraView> = UIHostingController( rootView: ExtraView( itemList: itemList ) ) addChild(extraViewController) // extraView は、あらかじめ Xib/Storyboard で空の UIView として Auto Layout 配置しておく extraView.addSubview(extraViewController.view) extraViewController.didMove(toParent: self) extraViewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ extraViewController.view.widthAnchor.constraint( equalTo: extraView.widthAnchor, multiplier: 1 ), extraViewController.view.heightAnchor.constraint( equalTo: extraView.heightAnchor, multiplier: 1 ), extraViewController.view.centerXAnchor.constraint( equalTo: extraView.centerXAnchor ), extraViewController.view.centerYAnchor.constraint( equalTo: extraView.centerYAnchor ) ]) } ) didMoveは忘れがちですが、処理の終了を通知するもので、しないとviewWillAppearなどライフサイクルに関係するメソッドが呼ばれなくなる可能性があるので、忘れないようにしましょう。 2. URLはClickableにする そしてURL対応。リンクを検出したら青文字・下線付きにして押せるようにしたいので、ExtraViewを以下のように作り変えました。 ExtraView.swift struct ExtraView: View { var itemList: [Item] = [] @ViewBuilder var body: some View { GeometryReader { geometry in VStack { ForEach(itemList, id: \.self) { item in HStack(alignment: .top) { Text(item.name) .frame(maxWidth: geometry.size.width * 0.12, alignment: .leading) Text(":") if item.isUrl { Button(action: item.actionUrl) { Text(item.value) .underline() .foregroundColor(Color.blue) } .buttonStyle(PlainButtonStyle()) .frame(maxWidth: .infinity, alignment: .leading) } else { Text(item.value) .frame(maxWidth: .infinity, alignment: .leading) } }.padding(.bottom, 1) } } .padding(.horizontal, 10) } } } struct ExtraView_Previews: PreviewProvider { static var previews: some View { ExtraView( itemList: ITEM_LIST_FOR_PREVIEW ) } } extension Item { private var url: URL? { return URL(string: self.value) } fileprivate var isUrl: Bool { if let url = url { return UIApplication.shared.canOpenURL(url) } return false } fileprivate var actionUrl: () -> () { return { if let url = url { UIApplication.shared.open(url) } } } } 変えたのは、if item.isUrlで開けるURLかどうかを判定する部分と、必要なExtensionの追加です。 ちなみに.underline()は1つ目に書かないとValue of type 'some View' has no member 'underline'というエラーに嵌ってしまいます。 おわりに そろそろ東京の大地が見えてきたので、書き終わります。 このように、SwiftUIはView1つから簡単に置き換えることが可能なので、大規模プロジェクトでもできるところから導入しましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xcode13から出るConditional downcast from 'String?' to 'String' does nothingについて

はじめに Xcode13 で以下のような不思議な警告が出るようになってたので共有しときます(原因わかる方いたらぜひ教えて下さい)。 Conditional downcast from 'String?' to 'String' does nothing 現象 やっていることは hoge?.first という String?? から String? にしているだけです。 let hoge: [String?]? = ["hoge"] let fuga = hoge?.first as? String print(fuga!) こちらのコードは Xcode12.5.1 では警告が出ないですが Xcode13.0 だと警告が出るようになっていました。 対応 とりあえず下記のように書くと警告は出ませんでした。 パターン1 let hoge: [String?]? = ["hoge"] let foo = hoge?.first let piyo = foo as? String print(piyo!) パターン2 let hoge: [String?]? = ["hoge"] let fuga = hoge?.first ?? nil print(fuga!) パターン3 let hoge: [String?]? = ["hoge"] let fuga = hoge?.first?.flatMap { $0 } print(fuga!) 不思議なのは警告がパターン1の foo as? String では出ないけど hoge?.first as? String で出ること(なんでだろう)。 おわりに けっきょく原因はわかりませんでしたがなんか不思議だなあということで記事にしてみました。 なんかわかる方いればぜひ教えて下さい Xcode のバージョンアップで今まで問題なかった書き方に警告が出るのはわりとよくある
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【続】iOSでAirtestを動かす

はじめに AirtestIDE でスマホアプリ(ゲーム)のテスト自動化に取り組んでいます。そこで実際に作成したスクリプトを実行する環境をつくるために実施したことを綴っていきたいと思います。 こちらは、昨年の記事「iOSでAirtestを動かしてみる」の続編になります。 前回課題となっていた横向きアプリへの対応が解決しましたので、そちらの情報共有と、当該環境を使用して本格的に取り組みを進めるにあたり、CLI環境からの複数端末での動作確認を行いましたので、いくつかの関連トピックを交えつつ共有できればと考えています。 環境 macOS Big Sur 11.6.1 Xcode 13.1 Airtest IDE 1.2.12(Python) iPhone XS(iOS 14.7.1) iPhone 8(iOS 15.1) セットアップ Step1 WebDriverAgent 前回紹介していた iOS-Tagent を Appiumの提供する WebDriverAgent に置き換えることで、横向きのアプリにも対応することができました。 WebDriverAgent をクローンします。 $ git clone https://github.com/appium/WebDriverAgent.git Xcodeで開きます。 ここで、PCに実機をUSB接続しておきます。 スキーム(ここでは「IntegrationApp」となっている箇所)をタップします。 WebDriverAgentRunner を選択し、先程接続したiOS端末をタップします。(ここでは「iPhone」となっている箇所) とりあえず、動かしてみます。 ワーニングを順次解消していきます。 次に、エラーを解消していきます。 WebDriverAgentRunner にて、Teamを選択します。(無い場合は AppleIDから作成) Product Bundle Identiferを設定します。(ユニークな文字列にする) 再度、ビルドします。 問題なさそうであれば、実機のスリープを解除した状態で、Testを実行します。 実機に WebDriverAgentRunnerがインストールされます。ここで、「設定」アイコンをタップします。 設定から「一般」をタップします。 「プロファイルとデバイス管理」をタップします。 WebDriverAgentRunner のデベロッパを信頼します。 PCに戻って、再度 Testを実行します。(手動で停止するまで実行中の状態となります) Step2 Set Ploxy iproxyをインストールします。 インストール $ brew install libimobiledevice iproxyを実行します。 実行 $ iproxy 8100 8100 実行中のメッセージ Creating listening port 8100 for device port 8100 waiting for connection Step3 AirtestIDE 接続情報を入力して「Connect」ボタンをクリックします。 iOS端末に接続されます。これでAirtestのスクリプトを実行することができるようになりました。 関連ノウハウ コマンドラインからの実行 CLIから実行することで、並行して複数端末での個別実行が可能となります。ここでは、2台の端末(iPhone XS、iPhone 8)を接続している状態を想定しています。 WebDriverAgentの複数起動 デバイスidを指定して複数起動する(それぞれ別枠で起動する) $ xcodebuild -project project/appium/WebDriverAgent/WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination 'platform=iOS,id=<iPhone XSのデバイスid>' test $ xcodebuild -project project/appium/WebDriverAgent/WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination 'platform=iOS,id=<iPhone 8のデバイスid>' test iproxyの複数起動 ポート番号とデバイスidを指定する(それぞれ別枠で起動する) $ iproxy 8100 8100 --udid <iPhone XSのデバイスid> $ iproxy 8200 8100 --udid <iPhone 8のデバイスid> AirtestIDE スクリプトの複数実行 ポート番号とデバイスidを指定する(それぞれ別枠で起動する) $ /Applications/AirtestIDE.app/Contents/MacOS/AirtestIDE runner "<スクリプト>.air" --device iOS:///localhost:8100//<iPhone XSのデバイスid> $ /Applications/AirtestIDE.app/Contents/MacOS/AirtestIDE runner "<スクリプト>.air" --device iOS:///localhost:8200//<iPhone 8のデバイスid> デバイスidの確認 デバイスidは idevice_id コマンドで確認できます。 セットアップ $ brew install libimobiledevice 実行 $ idevice_id アプリのインストール AirtestIDEの install() は iOS環境でサポートされていないので、ios-deploy を使うことにしました。 セットアップ $ npm install -g --unsafe-perm=true ios-deploy ipaのインストール $ ios-deploy --bundle <ipaファイル>.ipa インストール先のiOS端末を指定する場合は --id <デバイスid> を使います。 ipaのアンインストール $ ios-deploy --uninstall_only --bundle_id <バンドルid> スリープモードからの復帰 リモート環境から随時利用できるようにiOS端末がスリープ状態から復帰するコードを試してみました。 airtest home() sleep(2) home() text("<iOS端末のパスコード>") タップできないポップアップへの対応 アプリ初回起動時にiOSが出すダイアログが何故かタップできませんでした。しかし、一度アプリを終了すると当該ダイアログが残っており、タップできるようになりました。(テスト対象のアプリによるかもしれません) 参考 appium/WebDriverAgent macにUSB接続されたiPhoneの情報を取得する方法(libimobiledevice) おわりに 参考になった箇所などありましたでしょうか。 これをベースにiOSのテスト環境も構築していきたいと考えています。この記事が何かの助けになりましたら幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チュートリアル「AWS Amplifyを使用してシンプルなiOSアプリケーションを構築する」をやる際に隣で開いておくとヒントになるかもしれないページ

目的 このページでは、AWSから提供されている以下のチュートリアルを試行錯誤の末にクリアした時の情報を共有致します。 公式ページの情報が一部古く、今だと書いてある通りにはできない箇所もあったので、その辺りでどなたかの助けになればいいなと思って書きます。 前提 AWSのアカウントは既に持っていること Mac端末を持っていること Xcodeがインストール済みであること AnacondaもしくはDocker等で仮想環境を作成できること 私の環境 MacBook Pro (macOS Big Sur ver. 11.5.2) Xcode ver. 12.4 (12D4e) Anaconda Navigator 2.0.3 1/7 今回開発に使う仮想環境を作成する。 必須ではないと思いますが、クリーンな環境で開発した方が無駄なつまづきを防げます。 ライブラリやパッケージの依存関係ではできるだけ悩みたくないですからね。 ちなみに私は、Anacondaを使って「iOSAmplify」という名前の仮想環境を作成しました。Python 3.8です。 2/7 「はじめに」を実施する。 下記のリンクのページ内容を実施します。 公式ページの右側に、以下のような表示があると思います。 リンクになっている「手順をご確認ください。」をクリックしてください。GitHubのページが開きます。 Mac端末でTerminalを起動します。仮想環境切り替える必要がある方はお忘れなく。 Anacondaで仮想環境作った方は、以下のコマンドを実行すれば切り替えられます。 <>自体は入力する必要ありません。 conda activate <作った仮想環境名> Terminal上で、先ほど開いたGitHubのページに書かれているコマンドを brew install node まで実行します。 ここで、一つ確認してください。 Xcodeを起動して、Preferences -> Locations と開くと、 Command Line Tools という欄が空欄になっていないでしょうか? 空欄になっている方は、設定してください。私は、Xcode 12.4 (12D4e) を設定しました。 これが設定できていないと sudo gem install cocoapods がエラーになる場合があるようです。 この辺りで一度、端末の再起動をおすすめします。 さて、保留しておいた sudo gem install cocoapods を実行しましょう。 その後のバージョン確認も実施して、公式より新しいバージョンがインストールされていることを確認してください。 ちなみに、私の環境では以下のようになっていました。 # brew --verison Homebrew 3.3.4 # python3 --version Python 3.8.12 # aws --version aws-cli/2.4.0 Python/3.9.8 Darwin/20.6.0 source/x86_64 prompt/off # node --version v17.0.1 # pod --version 1.11.2 もし、sudo gem install cocoapods ができなかった場合は、brew install cocoapods でならインストールできるかも知れません。 gemは、Ruby言語用のパッケージ管理システムで、Homebrewは、MacOS用のパッケージ管理システムです。 3/7 「iOSアプリを作成する」を実施する。 下記のリンクのページ内容を実施します。 iOS アプリケーションを作成する Xcode起動 -> Create a new Xcode project を選択します。 公式では[iOS], [Application], [Single View App]を選択するように書かれていますが、 今のXcodeでは、[Single View App]ではなく、[App]と表示されていますので、[App]を選択して[Next]をクリックします。 その後は、公式に従います。プロジェクト名は自由につけてください。私は iOSAmplify としました。 言語とUIはそれぞれ Swift, SwiftUI としてください。設定できたら[Next]をクリックします。 Project Name: iOSAmplify Language: Swift User Interface: SwiftUI 公式に従い、ディレクトリを選択して、[Create]をクリックします。 そうすると、Xcodeでプロジェクトが開かれると思います。 メインビューを更新する ここは公式に従います。ContentView.swift を丸ごと置き換えて保存します。 構築およびテストする ここも公式に従います。 シミュレーターの選択の仕方が記載されていなかったので、補足します。 Xcode上で Product -> Destination -> 好きな端末を選択する。 という流れです。ちなみに私はiPhone SE (2nd generation) を選択しました。 4/7 「Amplifyを初期化する」を実施する。 下記のリンクのページ内容を実施します。 Amplify CLI をインストールする 公式に従います。 # Amplify CLI をインストール npm install -g @aws-amplify/cli # バージョン確認 amplify --version ちなみに、私の環境では、バージョンは7.4.4でした。 Amplify バックエンドを初期化する 公式に従って作業を進める前に、今回使うAWSのIAMユーザが用意できていない方は、作成しておきます。 既に作成済みのIAMユーザを使う場合は、Access key ID, Secret key ID をご用意ください。 厳密には、公式に書かれた手順を進めると、たとえIAMユーザが作られていなくてもTerminal上で作成させてくれます。ですので、それでも構いません。 以下、公式に従って作業を進めます。 補足です。 「ターミナルを開き、ディレクトリをプロジェクトディレクトリに変更します。」とありますが、要するに、Terminal上で cd をして3/7で作ったプロジェクトフォルダに移動してくださいという意味です。 移動した場所で ls -al コマンドを実行して、公式のような出力が確認できれば正しく移動できています。 次に、amplify init コマンドを実行すると、複数の質問が表示されてそれに回答する形で設定が進められていきます。質問が公式と異なる部分がありますので補足しておきます。 この後、実行するコマンドで選択ミスをした場合、ctrl + c で一度プロセスを停止して、再度 amplify init コマンドを実行すれば大丈夫ですよ。 ? Enter a name for the project # (自身のプロジェクト名)が薄く表示されていると思います。デフォルトのまま Enter キーを押してください。 # 次の質問が公式の2,3,4番目の質問をまとめたものになっています。 ? Initialize the project with the above configuration? # Environmentはdev, App typeはiosです。 # Default editorはお好きなものを選んでください。私は、Xcodeを選択しました。 # Default editorを変更したい場合は、一度 n で答えると一つずつ質問されて設定できます。 ? Select the authentication method you want to use # AWS profile ? Setup new user # 新規でIAMユーザを作りたい方は yes と答え、指示に従ってください。 # 私は既存のIAMユーザを使うため、No と答えました。 ? accessKeyId: # IAMユーザのAccess key ID を入力してください。 ? secretAccessKey: # IAMユーザのSecret access key を入力してください。 ? region: # お好きなリージョンを選択してください。 # 私は ap-northeast-1 を選択しました。東京リージョンです。 プロジェクトに Amplify ライブラリを追加する 公式にも書かれていますが、必ずXcodeを終了させてください。 pod init コマンドの実行まで公式に従ってください。 「c.Podfileを更新して次のポッドが含まれるようにしてから、プラトフォームを更新します。」を補足します。 この Podfile は、あなたのプロジェクトフォルダの中に作られています。このファイルを開いて、以下の作業をして保存します。 ・platform :ios, '9.0' の行をアンコメントし、13.0に変更 ・公式と同じ位置に「pod 'Amplify', '~> 1.0'」「pod 'Amplify/Tools', '~> 1.0'」を追加 公式に従って作業を進めてください。 xed . でXcodeが開かなかった方は、Xcode -> Preferences -> Locations -> Command Line Tools が設定されているか確認してください。 実行時に Amplify を初期化する 「a.Amplify 設定ファイルをプロジェクトに追加する」を補足します。 「awsconfiguration.json」「amplifyconfiguration.json」をXcodeプロジェクトにドラッグアンドドロップする際に、ポップアップ画面が出てきます。そこで「Create folder references」ではなく「Create Groups」が選択されていることを確認してください。 「b.実行時に Amplify クラスを読み込む」を補足します。 新しく作る「Backend.swift」は、「awsconfiguration.json」「amplifyconfiguration.json」と同じ階層に作ってください。 Xcode 12以降のお使いの方には、「AppDelegate.swift」は作られていません。代わりとなるのが「プロジェクト名App.swift」ファイルです。 このファイルを開き、ファイルの最後に以下のコードを追加してください。公式が追記してくださいと言っている Backend.initialize() を含んだ内容になっています。 class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // initialize Amplify let _ = Backend.initialize() return true } } 上記のコードを追加した直後、「Backend クラスが見つからない」といった類の警告が表示される場合がありますが、一旦無視して次の「設定を検証する」を実施すると警告が消えると思います。 設定を検証する うまく行きましたでしょうか? 5/7 「認証を追加する」を実施する。 下記のリンクのページ内容を実施します。 認証サービスを作成する amplify add auth コマンドを実行すると、質問が開始されます。 以下に、質問と回答を記載しておきます。 Do you want to use the default authentication and security configuration? # Default configuration with Social Provider (Federation) How do you want users to be able to sign in? # Username Do you want to configure advanced settings? # No, I am done. What domain name prefix do you want to use? # 何もせずデフォルトのまま Enter キーを押してください。 Enter your redirect signin URI: # gettingstarted:// ? Do you want to add another redirect signin URI # N Enter your redirect signout URI: # gettingstarted:// ? Do you want to add another redirect signout URI # N Select the social providers you want to configure for your user pool: # 何も選ばずに Enter キーを押してください。 認証サービスをデプロイする 特に書くことがありません。 ここまで順調に進んでおりますでしょうか? Amplify 認証ライブラリをプロジェクトに追加する ここも特に書くことがありません。 Podfile はあなたのプロジェクトフォルダの中にありましたね。 Amplify 認証ライブラリを実行時に設定する 「ビルド時に"development team"を選んでください」というようなエラーが出た方は、Xcodeでプロジェクト名(下の画像の部分です)-> Signing & Capabilities とクリックして Team を設定してください。 認証を実行時にトリガーします 「a.サインインとサインアウトのコードを追加バックエンドクラスの任意の場所に次の 3 つのメソッドを追加します。」を補足します。 公式で追加するように書かれているコードを、 private init() { と書かれた行の上に追記してください。 インデントは private init() { に合わせてください。 「b.認証ハブのリスナーを追加」を補足します。 下記のコードブロックの中で「// ここに追記してください。」の箇所に追記してください。 private init() { // initialize amplify do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.configure() print("Initialized Amplify"); } catch { print("Could not initialize Amplify: \(error)") } // ここに追記してください。 } } 「c.ユーザーインターフェイスコードを更新する」を補足します。 SignInButtonビューとSignOutButtonビューは「ContentView.swift」の最後に追記してください。 「e.ビルドとテスト」を補足します。 最初は、Sign up を選択すると思いますが、メールアドレスの入力の際に「@」が入力できない場合は、Shift + 2 で入力できます。 6/7 「APIとデータベースを追加する」を実施する。 下記のリンクのページ内容を実施します。 GraphQL API サービスおよびデータベースを作成する amplify add api コマンドを実行すると質問が開始されます。 下記に、質問と回答を記載しておきます。 ? Select from one of the below mentioned services: # GraphQL ? Here is the GraphQL API that we will create. Select a setting to edit or continue # Continue ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) ? Do you want to edit the schema now? # Y テキストエディタが開きますので、全体を公式に従って置き換えて保存してください。 クライアントサイドコードを生成する 特に補足はありません。 公式の動画がとても分かりやすいですね。 API サービスおよびデータベースをデプロイする amplify push コマンドを実行すると、質問が開始されます。 質問と回答を記載しておきます。公式とは異なる質問になっています。 ? Do you want to generate code for your newly created GraphQL API # Y ? Enter the file name pattern of graphql queries, mutations and subscriptions # デフォルトのまま Enter キーを押してください。 ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions # Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] # デフォルトのまま Enter キーを押してください。 ? Enter the file name for the generated code # デフォルトのまま Enter キーを押してください。 API クライアントライブラリを Xcode プロジェクトを追加する 特に補足はありません。 実行時に Amplify ライブラリを初期化する 特に補足はありません。 GraphQL データモデルとアプリモデル間のブリッジングを追加する 補足します。コードブロック中の「// ここに追記してください。」の位置に追記してください。 // the data class to represents Notes class Note : Identifiable, ObservableObject { var id : String var name : String var description : String? var imageName : String? @Published var image : Image? init(id: String, name: String, description: String? = nil, image: String? = nil ) { self.id = id self.name = name self.description = description self.imageName = image } // ここに追記してください。 } API CRUD メソッドをBackend クラスに追加する 「Backend.swift ファイルを開き、Backend クラスの末尾に次のスニペットを追加します。」を補足します。「クラスの末尾に」ということですので、「Backend.swift」の最後の } の一行上に追記してください。 メモを追加するための編集ボタンを追加する 「a.ContentView 構造体に、ユーザーインターフェイスにバインドされた状態変数を追加します。」を補足します。追加した後の構造体は以下のようになります。 struct ContentView: View { @ObservedObject private var userData: UserData = .shared @State var showCreateNote = false @State var name : String = "New Note" @State var description : String = "This is a new note" @State var image : String = "image" var body: some View { ZStack { if (userData.isSignedIn) { NavigationView { List { ForEach(userData.notes) { note in ListRow(note: note) } } .navigationBarTitle(Text("Notes")) .navigationBarItems(leading: SignOutButton()) } } else { SignInButton() } } } } 「b.ファイルの任意の場所に View 構造体を追加して、ユーザーが新しいメモを作成できるようにします。」を補足します。コードの末尾に追加してください。 「スワイプして削除」の操作を追加する 特に補足はありません。 構築およびテストする 特に補足はありません。 7/7 「画像保存機能を追加する」を実施する。 下記のリンクのページ内容を実施します。 ストレージサービスを作成する amplify add storage コマンドを実行すると質問が開始されます。 質問と回答を記載しておきます。 ? Select from one of the below mentioned services: # Content (Images, audio, video, etc.) ? Provide a friendly name for your resource that will be used to label this category in the project: # image ? Provide bucket name: # デフォルトのまま Enter キーを押します。 ? Who should have access: # Auth users only ? What kind of access do you want for Authenticated users? # create/update, read, delete を space キー で全て選択して Enter キーを押してください。 ? Do you want to add a Lambda Trigger for your S3 Bucket? # N ストレージサービスをデプロイする 特に補足はありません。 Amplify ストレージライブラリを Xcode プロジェクトに追加する 特に補足はありません。 Amplify ストレージプラグインを実行時に初期化する 特に補足はありません。 画像の CRUD メソッドを Backend クラスに追加する 「クラスの任意の場所に」ということですので、「Backend.swift」の最後の } の一行上に追記してください。 API からデータを取得するときに画像を読み込む 公式ページにも書かれていますが、8行目から17行目までを、ContentView.swiftの「convenience init」の中に追記します。 UI コードを追加して画像をキャプチャする 補足します。「CaptureImageView.swift」は、「ContentView.swift」と同じディレクトリに作成します。 メモが作成されたら画像を保存する 補足します。公式ページに書かれているコードは「ContentView.swift」に2箇所に分けて追記します。 // at the start of the Content View struct @State var image : UIImage? // replace the previous declaration of image @State var showCaptureImageView = false 上記のコードは、「ContentView.swift」の「AddNoteView」内の @State var image : String = "image" の行を置き換える形で追記します。 一方、 Section(header: Text("PICTURE")) { VStack { Button(action: { self.showCaptureImageView.toggle() }) { Text("Choose photo") }.sheet(isPresented: $showCaptureImageView) { CaptureImageView(isShown: self.$showCaptureImageView, image: self.$image) } if (image != nil ) { HStack { Spacer() Image(uiImage: image!) .resizable() .frame(width: 250, height: 200) .clipShape(Circle()) .overlay(Circle().stroke(Color.white, lineWidth: 4)) .shadow(radius: 10) Spacer() } } } } 上記のコードは、「ContentView.swift」で Section(header: Text("PICTURE")) { TextField("Name", text: $image) } の行を置き換える形で追記します。 次の「Create Note(メモの作成)セクションを変更して、画像とメモを保存します。」の部分は、「ContentView.swift」内の下記のコードを置き換える形で追記します。 Section { Button(action: { self.isPresented = false let noteData = NoteData(id : UUID().uuidString, name: self.$name.wrappedValue, description: self.$description.wrappedValue) let note = Note(from: noteData) // asynchronously store the note (and assume it will succeed) Backend.shared.createNote(note: note) // add the new note in our userdata, this will refresh UI self.userData.notes.append(note) }) { Text("Create this note") } } 構築およびテストする 問題なく「Run(実行)」できましたでしょうか? 複数のプロジェクト間でバックエンドを共有する 私は他のプロジェクトを作成しておりませんので、ここは省略します。 バックエンドを削除する 特に補足はありません。 終わりに 公式ページが更新されるまでの短い命の記事かもしれませんが、 どなたかのお役に立てたなら嬉しいです! ありがとうございました。 参考にした情報 [1] 公式ページ  参考にしたというか、このページをサポートする目的で本記事を執筆しました。 [2] [Xcode 12]アプリの起動について変更になった部分まとめ [3] UnityでiOSビルドするとエラーが出る
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Xcode】ファイルを作成する時のSwift FileとCocoa Touch Classの違い

初投稿です。 基本備忘録ですが、同じ疑問を持った方の為になれば幸いです。 今回はXcodeでファイルを作成する時に選択する「Swift File」と「Cocoa Touch Class」の違いをまとめてみました。 結論から申しますと、ファイル作成時にテンプレートがあるかないかだけの違いみたいです。 まず「Swift File」から見ていきましょう。 Swift File 「Swift File」を作成すると下記のような記述がされたファイルが作成されます。  ※コメント部分は省略しています。 import Foundation 上記のようにこの一行だけ記述されたファイルが作成されます。 「Cocoa Touch Class」 次に「Cocoa Touch Class」はどうでしょう。 「Cocoa Touch Class」を作成する場合は作成時にSub class(継承するクラス)を指定する必要があります。 ここではUITableViewControllerを指定しています。  ※ここでもコメント部分は省略しています。 import UIKit class TableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() } override func numberOfSections(in tableView: UITableView) -> Int { return 0 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 0 } } 上記の場合はUITableViewControllerを継承したので、必須なメソッドであるnumberOfSectionsとnumberOfRowsInSectionを元から記述してくれているということですね。 まとめ 「Swift File」は何も記述されていない白紙のファイルを作成したい時、「Cocoa Touch Class」は用途に応じて、継承させたいクラスがある時に使うというような解釈でいいと思います。 ここ違うよ!という場所などがありましたら、ご指摘いただければと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む