全45件 (45件中 1-45件目)
1
あまり見てらっしゃる方はいないと思いますが、このたびBloggerの方にお引越しすることにしました。https://miyatakeworks.blogspot.com/楽天ブログさんも無料で良かったのですが、ちょっとインターフェイスが使いづらいのと、広告が多すぎて視認性が悪いので、シンプルなブログサービスにお引越ししました。必要に応じて当ブログに記載した内容もBloggerの方に転載していきます。いままでありがとうございました。
2025.05.10
コメント(0)
「pyenvをインストールする」の続き。急ぎプログラムに着手したくて環境構築の時間をスキップするために一度 pyenv の使用を断念しました。でとった手段がどろくさい「シンボリックリンクを貼り替える」方法。python 3.x.xをインストールすると、「/usr/local/bin/」に python3 というシンボリックリンクが作成されます。私がもともと使っていたpythonは3.13.2 でしたが、chatterbot(というかspaCy)のインストール要件を満たすには python 3.12以下である必要があったためpython 3.12.9 を追加でインストールしました。ただ、python3 のシンボリックリンクは3.13.2を参照したままとなっていたため、一度シンボリックリンクを削除し、3.12.9を参照するように作り直します。手順:1./usr/local/bin の中身をちらみ(抜粋)pydoc3.12 python3.13 python3python3 だけではなく、python3.12、python3.13など各種バージョンのシンボリックリンクも存在します。2.古いリンクを削除sudo rm /usr/local/bin/python33.新しいPythonバージョンに対応するpipへのリンクを作成sudo ln -s /usr/local/bin/python3.12 /usr/local/bin/python3 ついでに、python という名前で 「python3.13.2」へのシンボリックリンクも貼っておきます。sudo ln -s /usr/local/bin/python3.13 /usr/local/bin/python4.バージョンの確認>> python3Python 3.12.9 (v3.12.9:fdb81425a9a, Feb 4 2025, 12:21:36) [Clang 13.0.0 (clang-1300.0.29.30)] on darwinType "help", "copyright", "credits" or "license" for more information.ついでにpipコマンドもやっておきます。1.pipを消すsudo rm /usr/local/bin/pip2.新しいシンボリックリンクを貼るsudo ln -s /usr/local/bin/pip3.12 /usr/local/bin/pip3sudo ln -s /usr/local/bin/pip3.12 /usr/local/bin/pipython3、pip3 はバージョン3.12.9python、pip はバージョン3.13.2になるように統一。これでひとまず、意識せずにpython3.12が使えるようになった。が、どう考えても無意味なのでもう一回pyenvをここみます。
2025.04.09
コメント(0)
当ブログを放置している間にMacBook Air M1の最終モデルを衝動買いしていました。んで、まっさらさらさらな環境にPython3.13.2がインストールしてあったのですが、chatterbotを利用するにあたり、3.13.2では spacy のインストールがうまくいかず、さてどうしようか?となったお話の第一弾。複数バージョンのpythonを併用するにはPyenvをインストールするといいよ、とCopilot君に教わったので、Supported by Copilotのもと pyenv をインストールしました。結論から言うと、、、pyenvは使わなかったんですけどね。手順1.brew をインストールするまっさらさらだったのでbrewからインストールします。/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install手順2.pyenv のインストール現在インストールされているpythonのバージョンを確認# Setting PATH for Python 3.13# The original version is saved in .zprofile.pysavePATH="/Library/Frameworks/Python.framework/Versions/3.13/bin:${PATH}"export PATHeval "$(/opt/homebrew/bin/brew shellenv)"brewを使ってpyenvをインストールします。brew install pyenv手順3.設定ファイルの編集通常、以下のファイルのいずれかを編集します(by Copliot君)~/.bash_profile (bashを使っている場合)~/.zshrc (macOSでは通常zshがデフォルトシェル)~/.bashrc私の場合はmacOSなので ~/.zshrc を編集します。~/.zshrc に以下の定義を追加します。export PYENV_ROOT="$HOME/.pyenv"export PATH="$PYENV_ROOT/bin:$PATH"eval "$(pyenv init --path)"それぞれの意味は以下の通り。export PYENV_ROOT="$HOME/.pyenv" → PYENV_ROOT という環境変数を作成し、Pyenv のインストールディレクトリ ($HOME/.pyenv) を指定しています。export PATH="$PYENV_ROOT/bin:$PATH" → Pyenv のバイナリ ($PYENV_ROOT/bin) を PATH に追加して、ターミナルで pyenv コマンドを直接使えるようにしています。eval "$(pyenv init --path)" → pyenv init --path を実行して、その結果を eval で適用しています。これにより、Pyenv の環境設定が適切に読み込まれます。設定を保存した後、以下のコマンドを実行して設定を反映します。source ~/.zshrc # または ~/.bash_profile4. pyenvでPythonバージョンを管理Pythonのインストール可能なバージョン一覧を確認します。pyenv install --list特定のバージョンをインストールする場合。pyenv install 3.12.9デフォルトのPythonのバージョンを指定する。pyenv global 3.12.9これにより、システム全体でPython 3.9.12を使用するようになります。特定のプロジェクトだけで異なるバージョンを使用する場合は、そのプロジェクトのディレクトリ内で以下を実行します。pyenv local 3.13.2たくぷれっさメモ:基本的に環境構築に多くの時間を割きたくないので、この辺の理解をおろそかにしてしまい、pyenvの使用を断念しました。ブログ書きながら整理できたので、今度リトライします。pyenvのインストールはここまで。先述の通り、結局pyenvの利用を断念したので、このあとアンインストールしました。次回は、pyenvを使わずに泥臭くpythonのバージョンを手動で切り替える編です。<参考にさせていただいたサイト>https://qiita.com/siakio/items/f9ab8a02d7b7fa6963ba
2025.04.09
コメント(0)
swift とかでも何度かハマったことがあるが、class A のインスタンスに class Bのインスタンの参照を持ち、かつ、class B のインスタンスにもclass A のインスタンスの参照を持ちたい時、classごとにソースを分けておくと、class の import 時に衝突が起きる。swift では、NotificationCenter を使って回避していたが、Pythonではこんな解決方法もあったよと言う話。programming by ChatGPT# ModuleA.pyclass MyClassA: pass# ModuleB.pyclass MyClassB: pass# 循環参照を避けるために関数内でimportdef some_function(): from ModuleA import MyClassA from ModuleB import MyClassB # MyClassAやMyClassBを使用する処理必要な時にだけ、class をインポートしてあげることができるらしい。こんな解決方法があるよと言うことは分かりつつ、PySide にもNotificationCenterのようなものがあるらしいのでそっちで解決予定。
2023.11.26
コメント(0)

PySide で横レイアウトと縦レイアウトを併用して、いい感じに WidgetをQMainWindow内に表示しようとしたがうまくいかなかった話。たタブ画面にQLabelとQLineEditを横に慣れて、かつそれを複数行に跨りたかったが、そもそも全く画面上に何も表示されなかった。解決方法だけ書くと、親Widgetを1つ作り、そいつをまず setCentralWidget でQMainWindowに追加し、その親WidgetにQHBoxLayoutを追加していく形で解決できた。↓のソースコードは中途半端だけど、一旦最低限やりたいことはできるようになったバージョン。class RequestTabContents(QMainWindow):# コンストラクタdef __init__(self): super().__init__() mainWidget = QWidget() # 縦方向のレイアウトを作成 vlayout = QVBoxLayout(mainWidget) # mainWidgetを中央に配置 self.setCentralWidget(mainWidget) ← これをやっていないことが原因だった self.layout = vlayout # mainWidgetにサブWidgetを追加していく # 1行目のレイアウトを作成 hlayout1 = QHBoxLayout() # URLの見出しとURLの表示 lblURL = QLabel('URL:') self.leURL = QLineEdit() hlayout1.addWidget(lblURL) hlayout1.addWidget(self.leURL) # 1行目のレイアウトを作成 hlayout2 = QHBoxLayout() # headerの見出しの表示 lblHeader = QLabel('Header:') self.leHeaders = QLineEdit() hlayout2.addWidget(lblHeader) hlayout2.addWidget(self.leHeaders) # vlayout に横レイアウトを追加していく vlayout.addLayout(hlayout1,2) vlayout.addLayout(hlayout2,8)た・だ・し、本来であれば leHeader をもっと幅広に表示したいのだが、1行分の高さしか持てていない。解決次第、当ブログ修正予定。ハマり時間 3hくらい(いまのところ)。
2023.11.25
コメント(0)
訳あって、Python でWebブラウザを作り始めた。最初にChatGPTに問いかけたときに PyQt5 で書かれたコードが吐き出されたので、それをコピペしつつ必要なライブラリをインストールした。が、開発過程でいろんなWebサイトを参照していたところ PySide2やら6 やら PyQt6 などなどが登場して一旦頭がショートした。一度は併存を考えたが、先々のことを考えるとあまり好都合では無さそうなので、スパッとPySide6 だけを使うことにした。pip コマンドでインストールしたライブラリは以下の通り。pip install pyside6とりあえず、超ミニマムな機能を持つブラウザが完成したので、やっとやりたいことに着手できる。ついでに、混乱過程でインストールしたさまざまなライブラリをアンインストールも実施。あーすっきりした。なお、ChatGPT にPyQt と PySide の違いを問いかけた時の回答は以下の通り。PyQtとPySideは、どちらもQtライブラリのPythonバインディングです。使用の好みによりますが、PyQtはGPLまたは商用ライセンスがあり、PySideはオープンソースでLGPLライセンスです。使いやすさは主観的な要素が強く、両方とも十分な機能を提供しています。個人的な好みやプロジェクトの要件に基づいて選択することが重要です。どっちでも同じようなことはできるのでお好みでどうぞ、と。
2023.11.24
コメント(0)

今回も約1日かけてmacOSのバージョンアップとXcodeのバージョンアップ終了。んで肝心のPythonのバージョンは?というと、、、Python 3.7.3 (default, May 11 2019, 16:05:57) [Clang 10.0.0 (clang-1000.11.45.5)] on darwinType "help", "copyright", "credits" or "license" for more information.変わってないんかーい。Pythonの公式サイトを見るとこんな文章があったので、とりあえずこちらをどうにかすることを諦めてPython3.12 をダウンロードすることに。Apple が提供している Python のビルドは /System/Library/Frameworks/Python.framework と /usr/bin/python にそれぞれインストールされています。これらは Apple が管理しているものであり Apple やサードパーティのソフトウェアが使用するので、編集したり削除したりしてはいけません。 インストール完了。Python Launcher なるものがインストールされた。こいつにスクリプトをドラッグすると、自分が選択した Python でスクリプトを実行してくれるらしい。macOSのバージョンアップもXcodeのバージョンアップも要らなかった。。。まあ、1年に1回くらいのイベントということで。https://www.python.org
2023.11.23
コメント(0)

XCode 15 のインストール要件が macOS13.5以上(だったかな?)だったので、まずOSのバージョンアップから開始。もともとがmacOS 12だったので2世代バージョンアップ。最近、まったくmacに触ってなかったので、1世代スキップしてしまったらしい。そして、なんか macOS14 の挙動に慣れない。デスクトップクリックすると全ウィンドウがデスクトップ外に飛んでいく。キモいので速攻で設定変更。設定>デスクトップとDockから「デスクトップとステージマネージャー」にたどりつく。壁紙をクリックしてデスクトップを表示を「常に」から「ステージマネージャ使用時のみ」に変更。ステージマネージャーってなんだって思って、一回設定をオンにしてみたが、キモかったので速攻OFF。設定がiOS寄りになっているのもなんか違和感がすごかった。まあ、数年前からOSのインターフェースの統合の話は言われてたけどグッと近づいてきたなって感じ。そして、地味に最近Macを使ってなさすぎて、文字入力を切り替えする時に間違えてCommandボタン+escボタンあたりを押してしまうのが悔しい。
2023.11.23
コメント(0)
最近、職場のVDIでPythonが使えるようになったので一気にPython熱が加熱。自宅のMacBookでもPythonが使えるように、しようということでちょっと動き出した。とは言っても、数年前に一度インストールしたことはあり、今般、改めて学習するに当たって最新版にアップデートしようかなということで、準備を開始。現在のバージョンPython 3.7.3 (default, May 11 2019, 16:05:57) [Clang 10.0.0 (clang-1000.11.45.5)] on darwinType "help", "copyright", "credits" or "license" for more information.バージョンアップ実行!brew upgrade pythonインストール後Python 3.7.3 (default, May 11 2019, 16:05:57) [Clang 10.0.0 (clang-1000.11.45.5)] on darwinType "help", "copyright", "credits" or "license" for more information.相変わらず3.7.3どうやら3.7.3 はXCodeのバージョンに引きずられている模様。$ python3.11 と明示的にバージョンを指定して起動すると、Python3.11が起動する。Python 3.11.6 (main, Nov 2 2023, 04:51:19) [Clang 14.0.0 (clang-1400.0.29.202)] on darwinType "help", "copyright", "credits" or "license" for more information. なんかキモい。とりあえず、XCodeをバージョンアップすることにした。最新の Python を Mac にインストールする方法
2023.11.17
コメント(0)

Mac の Safari から iCloud.com を開こうと思いアドレスバーに「iCloud.com」と入力したつもりが、何かを誤ったらしく下のようなサイトが表示されました。あからさまに詐欺です。何もボタンをクリックせずにスクショだけ撮って閉じました。あとからURLを調べようと思ったのですが、キャッシュに残っておらず調べられませんでしたが、アドレスバーに「icoud.com」と打ち込んでエンターキーをプッシュしたらそれっぽいサイトに飛びそうでしたので、多分これかなと。フリーダイヤル(笑)に表示されていた電話番号で検索したところ、ここ数日間で同じ電話番号を検索した人が2〜3人で始めた模様。なので、最近できたサイトなのかなと思っています。みなさまご注意ください。
2021.07.27
コメント(0)

新ゲーム「世界各国クイズ」を追加しました。お暇な時の時間潰しにどうぞ
2021.04.17
コメント(0)

朝会チャレンジのデバッグも終わり、いざAppStoreConnectへアップロードしようとしたのですが、今度はアーカイブ処理の最後で異常終了しました。エラー内容はスクショを撮り忘れたのですが、フレームワークのディレクトリが見つかりません的な内容でした。これは単純で、Podsプロジェクト内で、以下のようにリンク切れを起こしていると思われたファイルを消してしまっていたのですが、単純に消しちゃダメよ、っていう話でした。特に、Productsのフレームワークが見つからないよっていうエラーでした。もっとも初歩的な誤りだったのですが、それゆえにすごいハマりました。ハマり時間4時間くらい。Podでインストールしたものは、不要に見えても消してはいかん。
2021.04.12
コメント(0)
「Info.plist: No such file or directory」が解決したのも束の間、アプリケーション実行直後に以下のようなエラーを吐きました。---*** Terminating app due to uncaught exception 'GADInvalidInitializationException', reason: 'The Google Mobile Ads SDK was initialized without AppMeasurement. Google AdMob publishers, follow instructions here: https://googlemobileadssdk.page.link/admob-ios-update-plist to include the AppMeasurement framework and set the -ObjC linker flag. Google Ad Manager publishers, follow instructions here: https://googlemobileadssdk.page.link/ad-manager-ios-update-plist'terminating with uncaught exception of type NSException---AdMobの初期化やバナービューの作成箇所にブレークポイントを置いてみたのですが、どこにも引っ掛からず。AdMobの初期化はAppDelegateのdidFinishLaunchingで行っているため、それ以前のアプリケーションの初期化途中で起こっている模様。結果的には Info.plistに以下の定義を追加するだけで解決しました。---<key>GADIsAdManagerApp</key><true/>---ハマり時間30分くらい。今回参考にさせていただいた情報[Admob]起動時にアプリがクラッシュする
2021.04.12
コメント(0)

この2日間でいろいろありすぎて元の経緯を忘れたのですが、AdMobを iOS14 対応するために新しいSDKをインストールすることにしました。元々はAdMobのサイトからSDKをインストールしました。で、手動でライブラリを追加して、シミュレータで動作検証をしようとしたところ、コンパイルは正常終了するのですが、シミュレータへのアプリのインストール時に以下のようなエラーを吐き進めなくなりました。framework内のInfo.plist がないよ、という文字通りのエラーなのですが、手動だとうまくライブラリが紐づかないのかと思い、CocoaPod を使ってライブラリを再インストールしました。今回、CocoaPod のインストールをするのにもいろいろあったのですが、忘れなかったら自分のためにそのうちまとめます。無事CocoaPodの環境が整い、Podのインストールを行ったところ、今度は以下のようなエラーが出力されました。これは、リンケージがうまくいっていないためで、アプリケーションのTargetにライブラリを追加することで解決しました。そして、また冒頭のエラーに戻ったわけですが、結果的に下のようにすることで解決しました。CocoaPod のインストールは全く不要だったわけですね。ハマり時間3時間くらい。今回参考にさせていただいた情報Failed to load Info.plist from bundle
2021.04.12
コメント(0)
できるのかな、と半信半疑でコーディングしてみましたが、できました。// メッセージを送信するTimer.scheduledTimer(timeInterval: timerCount, target: TMGPSCommonModule.self, selector: #selector(timer_SendMessage(_:)), userInfo: message, repeats: false)timerCount += 0.7// appSyncに同時処理をさせないようにするため、一定間隔時間を開けてメッセージ送信を行う@objc class func timer_SendMessage(_ sender: Timer) { let appDelegate = UIApplication.shared.delegate as! AppDelegate let message = sender.userInfo as! TMGPSMessage appDelegate.dbProc.createMessageData(sndMsg: message, postName: .rcvmessage_sendMessage) return}GraphQLでは、CRUDの処理依頼から実行されるまでに若干のタイムラグがあるようです。ほぼ同時タイミングでappSyncClientに複数のCRUD操作を行うと、ある操作が何も処理をされないで終わるということがあり、そのためのWait処理を入れることにしました。appSyncClientを使いまわしているせいもあるかもしれないため、その辺は検証が必要かもしれません。ただ、同時接続数の問題もあるため、Clientを大量に作ればいいってものでもないでしょうし、みなさんどう解決しているんでしょう友人からの依頼で朝チャレへのゲーム追加をしないといけないため、記録係&AWSとの奮闘は一旦中断します。再開したとき全てを忘れていそうで怖い。
2020.11.30
コメント(0)
4月頃からコツコツ作っていたGPSロガーアプリ。 AppStoreに公開すべくAppStoreConnectで審査を依頼していましたが、無事却下されました。 なんでだ!?と思って審査結果を見たら、よくわからないからデモビデオを作れということらしい。 たしかにわからないよね、という感想と、そんなところまで見てるんだねっていう素直な驚き。 面倒くさいなぁ 2020/12/22 追記 デモビデオを作ってAppStoreConnectにアップロードし、再び申請したところ、1日程度で承認されました。 アプリ内の認証機能に不具合チックなものがあるので手動でテストしてるのであればまた却下かな、と思ってましたが、あっさり許可。 やっぱり機械的にテストしてるだけかなぁ 動画作成に要した時間 プライスレス
2020.11.30
コメント(0)
情報を取得するアプリ提供者みやたくワークス取得する情報と目的ゲーム実行情報の記録当アプリケーションでは、利用者の位置情報を記録し保存しますが、走行記録を取得し、客観的に表示することを目的としており、その他の目的では一切使用致しません。ユーザーのニックネーム・パスワード・所属グループ、および、走行記録・利用者の位置情報をAamazon Web Serviceのデータベースに保存し、同じグループに所属するユーザーに共有をしますが、グループ内での情報共有を目的としており、他のグループに所属する利用者には開示されることはありません。当アプリケーションでは、個人が特定できる情報、Wallet等の電子的なキャッシング情報は一切利用致しません。AWS プライバシー通知https://aws.amazon.com/jp/privacy/広告について当アプリケーションでは、広告配信ツールとしてAdMob(Google Inc.)を使用しており、AdMobがご利用者の情報を自動取得する場合があります。取得する情報、利用目的、第三者への提供等につきましては、以下の広告配信事業者のアプリケーション・プライバシーポリシーのリンクよりご確認ください。Google 広告に関するポリシーhttps://policies.google.com/technologies/ads?hl=ja
2020.11.28
コメント(0)
schema.graphqlに以下のような定義をしていました。パーティションキーに「circuitID」を指定しており、特にソートキーは指定していません。type TMGPSCourse @model @key(fields: ["circuitID"]) { circuitID : String! circuitName : String! isCircuit : Boolean! userEntry : Boolean! sectorCount : String! ldel_Flg : Boolean! ldel_Date : String!}このテーブルの内容を、何の条件もつけずにList形式でデータ取得すると、取得順序は保証されません。なので「sortDirectio」で、パーティションキーの昇順で取得しようと思って以下のようにプログラムしたのですが、取得結果が0件になりました。// データベースを検索する// 全件取得するappDelegate.appSyncClient?.fetch(query: ListTmgpsCoursesQuery(circuitID: nil, filter: nil, limit: nil, nextToken: nil, sortDirection: sort)) {(result, error) in 〜省略〜}ソートキーをつけていないせいかな、と思ったのですが、どうもそんなに簡単な話でもなかったようです。ソートキーは、あくまで同一パーティションキー内での並び順の指定であるようで、前述のようにパーティションキーを未指定の状態でソート順を指定すると、正しくレコードが取得できません。(結果が0件になる)↓↓のようにテーブルを作り直しても結果は変わりませんでした。# コース一覧テーブルtype TMGPSCourse @model @key(fields: ["circuitID","circuitName"]) { circuitID : String! circuitName : String! isCircuit : Boolean! userEntry : Boolean! sectorCount : String! ldel_Flg : Boolean! ldel_Date : String!}結局、プログラム側で、Arrayをソートして解決しました。ちょっと処理方法が強引なのでデータ量が増えてきたときに整合性が取れなくなりそうなので、改善必要。ハマり時間 1h
2020.11.19
コメント(0)
ユーザーと、1人以上のユーザーグループを作って簡易的なグループチャット的な機能を作っていました。 同一グループに所属するユーザー情報の一覧をデータベースから取得する際、ユーザー情報を全件取得して、同一グループのユーザーであるかを選別していたのですが、ユーザーの増加につれ取得件数が増加すること※と、ユーザー情報にパスワードも含んでいることから極力取得する情報を限定的にするために、グループ名をパーティションキーとしたユーザー一覧テーブルを作りました。 ※利用者が、友人枠をつかっても両手で数えたりそうなため不要な心配ですが・・・ そこで、ユーザー一覧テーブルに対して 所属するグループを変更した場合は過去に所属していたグループから自分の情報を削除する グループオーナーが新しいグループを作った場合は、グループに所属する全員の情報を削除する などの処理を作っていました。 ただ、これが削除処理を呼び出しているのにうまく削除されず。 デバッグしていてもperformを呼び出しているにも関わらず、その後の処理が実行されていませんでした。 appDelegate.appSyncClient?.perform(mutation: DeleteTmgpsGroupUserListMutation(input:mutationInput), resultHandler: { (result, error) in if let error = error as? AWSAppSyncClientError { self.errorCode = TMGPSDBProcess.DBERROR_ACCESS_ERROR self.errorMessage = error.localizedDescription } if let resultError = result?.errors { self.errorCode = TMGPSDBProcess.DBERROR_USERLISTDELETE_ERROR for error in resultError { self.errorMessage! += "エラーコード:" + String(error.errorDescription!) self.errorMessage! += "\nエラー詳細:" + error.message + "\n" } } if ( self.errorCode == TMGPSDBProcess.DBERROR_NO_ERROR ) { // エラーが起きていない場合 self.statusCode = TMGPSDBProcess.DBERROR_USERLISTDELETE_NORMALEND self.errorMessage = DBErrorTitle.DBERROR_USERLISTDELETE_NORMALEND.rawValue } //グループユーザー一覧情報の削除結果を通知する NotificationCenter.default.post(name: postName, object: nil) }) ※resultHandlerの内側の処理が全く呼び出されなかった。。。 まだ、理由が特定できていませんが、 いろいろな処理を同時に実行しすぎて、AppSync が処理し切れていなかったせいぽいです。 1つのDBアクセスが終了するまで次のアクセスを行わないようにプログラムを整理をしたところ、ちゃんと全て動くようになりました。 データベースへのコネクションを一つしか作っていなかったので当たり前と言えば当たり前ですかね。 ハマり時間 4時間
2020.10.29
コメント(0)
GraphQL でDynamoDB からデータを取得する場合、Query を使います。プライマリキーを指定して、特定の1件だけを取得する場合はGetテーブル名Queryを使用します。例では、groupName がパーティションキー、userID がソートキーです。appDelegate.appSyncClient?.fetch(query:GetTmgpsGroupUserListQuery( groupName: groupName, userID: userId)) 次にテーブルにあるデータを全件取得する場合は、Listテーブル名sListQueryを使用します。キーを何も指定しなければ全件取得できます。// groupNameでデータベースを検索するappDelegate.appSyncClient?.fetch(ListTmgpsGroupUserListsQuery())最後に、ソートキーを指定せず、特定のパーティションキーのデータを全件取得する場合ですが、Listテーブル名sListQueryを使用するのは変わらず、パーティションキーだけを指定し、他の引数は全てnilにするだけです。 // groupNameでデータベースを検索するappDelegate.appSyncClient?.fetch(query:ListTmgpsGroupUserListsQuery( groupName:appDelegate.GroupData.groupName, userID: nil, filter: nil, limit: nil, nextToken: nil, sortDirection: nil)これにめっちゃはまり時間を浪費しました。API.swift 上、ListTmgpsGroupUserListsQueryのイニシャライザは以下の通り定義されています。public init( groupName: String? = nil, userID: ModelStringKeyConditionInput? = nil, filter: ModelTMGPSGroupUserListFilterInput? = nil, limit: Int? = nil, nextToken: String? = nil, sortDirection: ModelSortDirection? = nil){ self.groupName = groupName self.userID = userID self.filter = filter self.limit = limit self.nextToken = nextToken self.sortDirection = sortDirection}引数を省略したら 自動的に nil を与えるよ、っていう定義です。上記の2つ目のケースは、引数を1つも与えないことで、暗黙的に引数にnilが与えられることによってパーティションキー、ソートキーの指定がされずに全件データを取得しているのです。つまり、、、パーティションキーだけ値をセットして、他の引数は全て明示的にnilを与えてあげればそれでよかったのです。これに気づかずにどはまりしました。ちなみに、ソートキーは以下のようになっています。input ModelStringKeyConditionInput { eq: String le: String lt: String ge: String gt: String between: [String] beginsWith: String}いまいち使い方が理解できていませんが、論理演算で該当するレコードだけが取得できそうです。その他、取得件数の制限や、フィルター、ソート順の指定(昇順、降順)ができそうです。今のところ、ソートしかちゃんと使いこなしていないです。ハマり時間 4時間。。。なんか、Safariのせいなのか、楽天ブログのせいか分かりませんが、文字を変換途中に勝手に変換終了しやがってうざい。見たまま編集 off たらなおったのでブログのせいかな。
2020.10.29
コメント(0)
push 時にこんなエラーが発生しました。Attempting to edit the key schema of the TMGPSCourseTable table in the TMGPSCourse stack. An error occurred during the push operation: Attempting to edit the key schema of the TMGPSCourseTable table in the TMGPSCourse stack. プライマリキーは変えられないよ!的なエラー。そりゃそうですね。というわけで、一回データソースごと削除して再作成しました。AWS上でテーブルを削除しても、データソースが残ってると同じエラーを吐き続けるのでご注意ください。データソースの削除は、schema.graphql から定義を削除してから push するだけです。こんな感じでコメントアウトしてあげるだけでも大丈夫です。#type TMGPSImgMessage @model {# id : ID!# imageName : String!# sendType : String!# message : String!#}削除が終了したら、改めて新しいテーブル定義で push push!以上。ハマり時間 1hGraphQLのコメントアウト方法、今までちゃんと調べてなかったので今日初めて知りましたwww
2020.10.27
コメント(0)
以前にAmplify + GraphQLでテーブルを作成した際は、あまり深いこと調べなかったので、プライマリーキーは必ずid : ID!にしないといけないのか、程度にしか認識してなかったんですが、そんなわけないだろ、と改めて思いちゃんと調べました。type テーブル名 @model @key (fields: ["パーティションキー"] )type テーブル名 @model @key (fields: ["パーティションキー","ソートキー"] )で設定できるようです。1つ目の例では、パーティションキーがプライマリキーになります。2つ目の例では、パーティションキー+ソートキーがプライマリキーになります。以下、実例。1.パーティションキーがプライマリキーのパターンtype TMGPSUser @model @key(fields: ["userID"]) { userID : String! userName : String! useCloud : Boolean! userPass : String! joinGroup : Boolean groupName : String groupPass : String appVersion : String! passLimit : String! ldel_Flg : Boolean! ldel_Date : String}2.パーティションキー+ソートキーがプライマリキーのパターン# Groupに所属しているユーザー一覧type TMGPSGroupUserList @model @key(fields: ["groupName","userID"]) { groupName : String! userID : String! userName : String!}なお、id : ID! でパーティションキーを設定している場合は、以下のようにすることで、パーティションキー+ソートキーをプライマリキーにできます。(たぶん)# サンプルテーブルtype TMGPSSample @model @key(fields: ["sortKey"]) { id : ID! sortKey : String! property : String!}ちなみに、以下のようにすると @key 指定しなくてもパーティションキーとソートキーをプライマリキーにできるはず。試してないから断定できないけど。。。# サンプルテーブルtype TMGPSSample { id : ID! sortKey : ID! property : String!}
2020.10.27
コメント(0)

ビューコントローラー内のオブジェクトを明示的に廃棄しないと、ゴミのようなコントローラーが残っちゃいますよ、というお話です。 先日作成した簡易的なメッセージ送受信画面(ビュー)。 テキストフィールドにメッセージを書き込むか、ビュー下段にあるボタン(イメージビュー)をタップすると、クラウド上のデータベースにメッセージが登録され、受信者&送信者は一定間隔でデータベースからメッセージを受信するという仕組みです。 受信したメッセージは、画面の上段の受信用イメージビューとメッセージラベルにそれぞれ表示されます。 テストをしていたところ、1回目のビュー表示時は正しくメッセージが送受信されるのですが、2回目以降のビュー表示時は、メッセージを送信してもその結果が反映されない事象が発生しました。 先に結論を言ってしまうと、前ビューに戻る際にコントローラー内で使用しているTimerオブジェクトが動きっぱなしになっており、そのせいでコントローラーが廃棄されていないことが原因でした。 表示中のビューに紐づいているコントローラーと、イベントを受けるコントローラーが別々になってしまっていたせいでラベルなどの更新がうまくいっていませんでした。 今後同じツボにはまらないように、調査の中で得た知見等をまとめておきます。 調査の過程 事象を発見した直後に、ビューの動作とデバッガーからわかったことは以下の3点。 ・2回目以降も初期表示時は正しくイメージとラベルは表示される ・送信したメッセージはデータベースに格納されている ・受信処理も正常終了していて、メッセージを格納する配列にも正しく登録されている 以上のことから、メッセージ送受信の仕組みは正しく動いており、ビューの再描画ができていないことがは結構早い段階でわかりました。 ビューに対して、setNeedsDisplay() 発行してみても変わらず。 Debug用にUILabelを継承した以下のようなクラスを作ってみました。 import UIKit// UILabelのデバッグ用。class TMLabel : UILabel { fileprivate let color = UIColor.white // プロパティの初期化 fileprivate func configure() { backgroundColor = color translatesAutoresizingMaskIntoConstraints = false } /// イニシャライザ init() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.init(frame: CGRect.zero) configure() } /// イニシャライザ override init(frame: CGRect) { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.init(frame: frame) configure() } /// イニシャライザ(StoryBoardやIntefaceBuilderでの生成) required init?(coder aDecoder: NSCoder) { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.init(coder: aDecoder) configure() } /// 制約の更新 override func updateConstraints() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.updateConstraints() } /// 制約の更新要否のフラグを立てる override func setNeedsUpdateConstraints() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.setNeedsUpdateConstraints() } /// 必要に応じて制約の更新を即時実行する override func updateConstraintsIfNeeded() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.updateConstraintsIfNeeded() } /// レイアウトの更新 override func layoutSubviews() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.layoutSubviews() } /// レイアウトの更新要否のフラグを立てる override func setNeedsLayout() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.setNeedsLayout() } /// 必要に応じてレイアウトの更新を即時実行する override func layoutIfNeeded() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.layoutIfNeeded() } /// 描画の更新 override func draw(_ rect: CGRect) { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.draw(rect) } /// 描画の更新要否のフラグを立てる override func setNeedsDisplay() { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.setNeedsDisplay() } /// 描画の更新要否のフラグを立てる override func setNeedsDisplay(_ rect: CGRect) { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) super.setNeedsDisplay(rect) } deinit { print(NSStringFromClass(type(of:self)).components(separatedBy: ".")[1] + ":" + #function) }} 実行結果はこんな感じ。(ラベルが3つあるので三重にダンプされてます) 1回目・ビュー初期化時TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:setNeedsLayout()TMLabel:setNeedsLayout()TMLabel:setNeedsLayout()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:draw(_:)TMLabel:draw(_:)TMLabel:draw(_:) 1回目・メッセージ送信から受信時後TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:layoutSubviews()TMLabel:updateConstraints()TMLabel:updateConstraints()TMLabel:layoutSubviews()TMLabel:draw(_:)TMLabel:draw(_:)TMLabel:draw(_:) 2回目・ビュー初期化時TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:init(coder:)TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:layoutSubviews()TMLabel:draw(_:)TMLabel:draw(_:)TMLabel:draw(_:) 2回目・メッセージ送信から受信時後TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay()TMLabel:setNeedsLayout()TMLabel:setNeedsDisplay() 2回目のビュー初期化時まではdraw()が呼び出されているが、メッセージ送受信後はsetNeedsDisplayは呼び出されているものの、drawが呼び出されていないことがわかりました。 ラベルのメモリ上の番地のダンプ結果はこんな感じでした。 1回目・ビュー初期化時 1回目・メッセージ送信から受信時後 2回目・ビュー初期化時 2回目・メッセージ送信から受信時後 これでやっと原因が分かりました。 2回目初期化時は番地が変わっているものの2回目受信時には1回目と同じ番地になってます。 つまり、2回目のイベントを受け取っているコントローラーは1回目と同じコントローラーでした。 概念的にはこんな感じ。 ※iPhoneのメモ帳で手書きしたので全体的に汚くてすみません。 XCodeでメモリの状態を表示してみたところ、本来1つしか生成されないはずのビューコントローラーが2つあるのが分かります。 コントローラーが廃棄されない原因は冒頭で書いた通り、コントローラー内で使用しているTimerが生きて続けていたことでした。 前ビューに戻る操作を自分で実装し、アクション内でTimerを廃棄する処理を生成したら、無事コントローラーが破棄されるようになった。 // MARK: - IBAction // 戻るボタンタップ時の操作 @IBAction func action_BackButton() { // メッセージ受信タイマーを初期化 if( self.msgRcvTimer != nil ) { self.msgRcvTimer.invalidate() self.msgRcvTimer = nil } // ビューのClose用のタイマーを初期化 if( self.closeTimer != nil ) { self.closeTimer.invalidate() self.closeTimer = nil } // 前の画面に戻る self.navigationController?.popViewController(animated: true) return} わかってみたら、当たり前のような話ですが、だいぶはまりました。 解決時間ほぼ3日。
2020.10.24
コメント(0)
![]()
お風呂ってカビ生えますよね。いきなりなんだよって話ですが。カビの種類にも赤カビ、黒カビがありますが、私の場合は黒カビが生えてきたらカビキラーでカビ退治します。赤カビは最悪ゴシゴシすれば落ちるので。ただ、カビキラーでカビ退治しただけではすぐにカビが生えてきますよね。そこで、必ず「おふろの防カビくん」を使って防カビ対策します。これ、地味に優秀で、1回やっておけば1〜2ヶ月はカビ知らずです。1〜2ヶ月としているのは季節や天気によるからです。お風呂の場合、特に浴室乾燥機能を使うとカビが繁殖しやすくなるような気がします。梅雨や台風シーズンなどで外に洗濯物が干せず、浴室乾燥機能を連日使うと最短1ヶ月くらいしか効果が続きません。そうでもなければ、2ヶ月くらいはもつかな〜、という体感です。本当は、1ヶ月毎または1ヶ月半ごとに防カビくんを使っていればカビキラーすら必要ないんじゃないかと思うのですが、そこは持ち前のだらしなさで結局毎回黒カビを生やします。みなさんはそうならないように、こまめに防カビくん施工してくださいね。ライオン(LION) ルックおふろの防カビくん煙剤 3P楽天で購入今日ホームセンターで買ってきましたが、楽天さんちょっとお高めですね。ホームセンターで100円は安く買えました。
2020.10.19
コメント(0)
ただの備忘録です。NavigationController の制御下で前画面に戻る場合は、自オブジェクトで保持しているNavigationController オブジェクトに対して、popViewController を発行する。// 前画面に遷移するself.navigationController?.popViewController(animated: true)NaviController 制御かで Show segue で遷移する場合とかですね。 対して、NavigationController の制御以外で次画面を閉じる場合は自オブジェクトに対して dissmiss メソッドを発行する。// 前画面に遷移するself.dismiss(animated: true, completion: nil)Present Modally でビューを開いた時とかですね。ちなみに、ビューが閉じる時に、オブジェクトの初期化等を行いたいような場合は、viewWill(Did)Disappearイベント内で isBeingDismissed メソッドを発行します。true が帰ってきた場合、ビューを閉じようとしていると判断できます。// ビューが非表示になる直前に呼び出されるoverride func viewWillDisappear(_ animated: Bool) { if ( self.isBeingDismissed == true ) { // 処理を記述する }}ただ、これだと NavigationController で Popした場合検知できないので、それは調べ次第追記します。(そういうケースが出てきた場合に。。。)
2020.10.19
コメント(0)

下のようなメッセージ送受信画面を作っています。(画面がしょぼいのはご勘弁ください)自分宛のメッセージの受信や、自分が送信したメッセージ※を最新の3回分のメッセージを表示したかったのですが、最新のメッセージの受信がうまくいかない。※自分が送信したメッセージも一度データベースに登録した後に受信する仕組み状況としては、アプリ起動時に、データベースに溜まっているデータは全て取得できるメッセージ送信ボタンをタップするとメッセージはデータベース上に正しく登録されている以上のことから、送受信のロジックそのものは正しく動作しているっぽい。ただ、最新のメッセージが表示されない。なんでかなぁと思い、appSyncClient のパラメータやメソッドをのぞいていたら「clearCaches()」というメソッドを発見。これか?というわけで、下のようなロジックをつくってみた。// キャッシュの初期化を行う// 返り値:// true : 初期化成功// false: 初期化失敗func clearCache() -> Bool {var result : Bool = true//appDelegateを取得するlet appDelegate = UIApplication.shared.delegate as! AppDelegate// データ取得前にキャッシュを初期化するdo {try appDelegate.appSyncClient?.clearCaches()} catch {self.errorCode = TMGPSDBProcess.DBERROR_CACHE_CLEAR_ERRORself.errorMessage = error.localizedDescriptionresult = false}return result}で、こいつを、fetch の前で実行。// データ取得前にキャッシュを初期化するif ( self.clearCache() == false ) {NotificationCenter.default.post(name: postName, object: nil)return}// データベースを検索する// 全件取得するappDelegate.appSyncClient?.fetch(query: ListTmgpsMessagesQuery()) {(result, error) inif error != nil {〜 以下省略 〜動いた!念のため、get系の処理の前に全てこのロジックを追加しました。解決時間2hハマった。
2020.09.29
コメント(0)

DynamoDBに対してCreate指示したら下図のようなエラーが発生した。keyとなる項目”id” に、本来は文字列”S”(例えば)が 期待されるところ、NULLが入っているから不正だよ、というエラー。そりゃそうだ。だって、セットしてないもん。エラーはいた時点でのGraphQLの定義。明示的に変数 id は定義していなかった。修正後。id を定義して、プログラムからも明示的にユニーク値をセットするようにした。解決。解決時間1h微妙にハマった。英語から逃げてはいけないという誰かの言葉を思い出しました。
2020.09.29
コメント(0)

めっちゃはまった話。amplify push しようとしたらこんなエラーが発生した。UPDATE_FAILED GraphQLAPIKey AWS::AppSync::ApiKey Fri Jun 26 2020 13:13:02 GMT+0900 (GMT+09:00) API key not found: api-key名 (Service: AWSAppSync; Status Code: 404; Error Code: NotFoundException; Request ID: request id)AppSyncのAPI Key はデフォルト設定だと7日で有効期限が切れるらしく、AppSync コンソールの「設定」からAPI Key を再生成した。再生成したAPI Key をプロジェクトに反映しようと思い、旧API Keyでgrep したら以下の4ファイルがヒット。./awsconfiguration.json./amplifyconfiguration.json./amplify/backend/amplify-meta.json./amplify/#current-cloud-backend/amplify-meta.json※./ はプロジェクトのルートディレクトリ一つ一つ更新し、再度 push。すると同じエラーが発生した。なんで?って思ったら下の2つが元に戻っている。いろいろコマンド発行してみたけど、結局治らず。しかたなく、pod のインストールからやり直すことに。今から思えば、せいぜい amplify init あたりからでよかったような。。。$ amplify initinit 直後の awsconfiguration.json特段有益な情報は付加されていない。amplify-meta.json のスクショ取り忘れる。$ amplify add apiadd api 直後の awsconfiguration.json。特に変化なし。同じく amplify-meta.jsonapi に関する情報はなし。また一週間後に同じことをやるのは面倒なので、有効期限を365日に設定。? After how many days from now the API key should expire (1-365): 365$ amplify pushpush 後の awsconfiguration.json。認証情報が付加された。同様に amplify-meta.json。モザイクだらけですが、API情報が付加されている。コマンドの終了直前に、コンソール上に API KEY が表示されるあたりからも、ここでAPI Key が割り振られている模様。結局、API Key の有効期限が切れたらどうすればいいのかわからず。いったん各ファイルからAPI Key に関わる情報を消してしまえばいいのか?有効期限を延長したので、ハマるのはまた来年。
2020.06.26
コメント(0)

引き続き、AWSとの格闘?の記録です。テーブルを作ったのでiOSアプリから接続できるようにしよう!ということで、チュートリアルを元に環境構築を開始しました。ここで結構ハマり、まず環境構築を開始するまでに2〜3日が経過しました。※そもそも、ドキュメントちゃんと読めよって話なんですがね。。。一応、経緯だけ書いておくと、DynamoDBのドキュメントには、Mobileアプリから呼び出す場合は「Mobile Hub」というページからプロジェクトを作ってね、と書いてあるのですが、現在は、実際そのリンクに飛ぶと「Amplify」という仕組みのページに飛んでいきます。結論から言うと、この AmplifyというCLIツールを使用して環境構築が全て行えるのですが、そこを理解するのに時間がかかり、何をすれば Mobile Hub のページにたどり着くんだ!?という無駄な時間をすごしました。おそらく、Mobile Hub は現行ユーザーは使用できるけど、新規の人は Amplify 使ってねっていう、Amazonさんの親切心だったんでしょう。が、ハマりました。というわけで、以下 Amplify のインストールから、XCodeのプロジェクトにリソースをインストールしていくところまでの流れです。まず、Amplify のトップページです。https://us-east-2.console.aws.amazon.com/amplify/home?region=us-east-2#/GET STARTED から進みます。なんか、日々、トップ画面とかチュートリアルが変わっているような気がするんですが、そこは気にせず。こんなページに進むので、迷わずiOSを選択します。チュートリアルに進みます。なんでいきなりチュートリアル?みたいな感じで、ここも嵌る原因の一つとなったのですが、結果的に Amplify のインストールはこの手順通りに進めていけば全てうまくいきます。要は、Amplify が 環境構築のためのツールだと理解できなかった故にはまっていたわけでした。チュートリアルの内容は、コマンドをベースに書き進めていきますが、大前提として以下のツールが必要があるため、ない場合はインストールします。CocoaPods と Amplify CLI ってやつが、ターミナルから環境構築をお手伝いしてくれるツールです。役割を理解すると便利です。Amplify CLI は以下のコマンでインストールできます。$ sudo npm install -g @aws-amplify/clisudo コマンドで発行しないと権限不足でエラーになる可能性があります。amplify CLI のインストールが終わったら、いざ環境構築。まず、XCodeでプロジェクトを起動している場合は終了しましょう。また、チュートリアルにはIAMユーザーの作成手順がありますが、割愛します。チュートリアル通りに行えば簡単に作成することができます。1.プロジェクトのディレクトリへ移動以降の手順は、XCodeのプロジェクトのディレクトリで実行していく必要がありますのでcdコマンドで移動します。$ cd プロジェクトディレクトリのパス2.CocoaPodsマネージャでプロジェクトを初期化$ pod initPodfile というファイルが作成されます。中身はこんな感じ。使用したいPod(ライブラリ)を追記します。3.Podfile に記述した pod(ライブラリー等々)のインストール以下のコマンドでPodのダウンロードが行われます。$ pod install --repo-updateプロジェクトフォルダ内にPodと、workspaceファイルが作成されます。以降は、xcodeprojファイルではなく、このxcworkspaceファイルを使ってプロジェクトを開きます。Podsフォルダの中はこんな感じ。以下のコマンドでワークスペースファイルが開きます。普通にダブルクリックでも開ますが、なんか手順にあるんで。$ xed .4.プロジェクトにAmpify Tools を追加します。ちょっとここの目的が理解できてないです。TARGETS に 「New Run Script Phase」を追加します。新しくできた「Run Script」を「[CP]Check Pods Manifest.lock」の下に移動します。以下のコマンドを追加します。"${PODS_ROOT}/AmplifyTools/amplify-tools.sh"名前を「Run Amplify Tools」に変更。プロジェクトをビルドしてこの手順は終了。5.Amplifyの初期化を行います。$ amplify initamplifyconfiguration.jsonとawsconfiguration.jsonをプロジェクトに追加します。※チュートリアルの手順を何度も実行しては最初からやり直してを繰り返していたので、定かではないのですが、4の手順実行しておくとこれ必要ないかもしれません。手動で追加する場合は、プロジェクト名と同一のフォルダの直下に配置します。6.AppSync API を作成します。(多分)$ amplify add api実行時、いくつか質問されますが、基本的にはデフォルト設定で問題ありません。AppSyncコンソールを開くと、APIが追加されています。7.schema.graphqlファイルの編集今までの手順(よく覚えてないけど、多分6)で、schema.graphqlというファイルが作成されます。このファイルは、DynamoDB(に限らないかもだけど)のテーブル定義を記述します。type テーブル名 @model { 項目名 : 型名}となります。@model は、テーブルを作成することを意味します。これをつけないと、テーブルは作成されず、オブジェクト型のデータとして認識させることができます。また、型名の後に ! をつけるとNULL不可になります。IDは、実質、String型らしいですが、ユニークキーとして認識しています。(間違えているかも)テーブルと、テーブル内で使用するオブジェクト型の定義下では、オブジェクト「TMGPSSector」のリスト(配列)を定義しています。7.AppSync にスキーマを登録する。以下のコマンドで、AppSync にスキーマを登録することができます。$ amplify pushここでもいくつか質問されますが、基本的にデフォルトでOKです。正常終了すと、AppSyncのデータソースにテーブルが追加されます。データソースからリソースのリンクをクリックしているとテーブルが作成されていることがわかります。結果、旅立ちへんで作成したテーブルはまったくもって不要となったわけでした。また、プロジェクトフォルダ内に「API.swift」というファイルが作成されますので、これをXCodeプロジェクトのプロジェクト名と同じ名前のフォルダに追加します。「API.swift」ファイルは、プログラム(GraphQL)からDynamoDBを操作するためのAPIが全て記述されています。データソースを作り替えても、「$amplify push」を実行することでこのファイルにも自動的に反映されます。詳細は、プログラム編で記載しようと思います。次回は、Swift からテーブルの読み書きをする方法を記載しようと思いますが、現在、プログラム中のため、いつ頃になるかわかりません。以下、データソースの登録中吐いたエラーと、気づいたことをだらだら書いておきます。テーブル名に_(アンダーバーが入っているとエラーになるっぽい)Template format error: Resource name TMGPS_BestLap is non alphanumeric.コメントに使ってた // はエラーになるSyntax Error: Cannot parse the unexpected character "/".※swiftのstruct定義をそのままコピーしたので。。。? もついてるとエラーになるSyntax Error: Cannot parse the unexpected character "?".Double型は存在せず、Floatにする必要があるThe input value type 'Double' is not present when resolving type 'TMGPSLapInput' [@621:1].Bool じゃなくて、BooleanUnknown type "Bool". Did you mean "Boolean"?※これもstruct型からコピペしたんでエラー吐きました。schemaに項目の追加、削除等を行って push すると自動的に API.swift は更新されるが、たまにゴミ項目ができる時があるので、そのときは造りなおしたほうが良い?id 、 登録日、変更日は、schema に定義しなくても、勝手に付加されているぽい。これ、ちょっとびっくりしました。最初、自分で定義していたんですが、不要みたいだったので登録日と変更日は削除しました。idはまだ理解できていないので、わかったら追記します。
2020.06.02
コメント(0)

随分と更新をサボりましたが何もやっていなかったわけではなく、むしろネタが結構あります。 ここ最近、突っ走りまくっていて振り返る時間を作っていなかったのですが、私のような頭も悪い記憶力もない人間は、一旦立ち止まって振り返ることが必要だな、と思い記録します。 4月上旬くらいから朝会チャレンジのバージョンアップを一旦中断し、過去に作っていたCoreLocationを使ったアプリの焼き回しをしていました。 で、データベースが欲しいなと思っていたのですが、自分でサーバー立てるのも主にセキュリティ関係で自信が持てなかったので無料のDBクラウドサービスないかなって探した結果、たどり着いた先が AWS の DynamoDBでした。 こいつも特定の条件下では無料だよ、ってレベルなのですがそこの範囲で収まるように使おうと考えたわけです。 結果、毎月30円弱ずつ請求されているわけですが、そこはまたおいおいと。 DynamoDB は、NoSQLという、非リレーショナルデータベースに該当します。 NoSQLの中でもDynamoDBは、JSONでデータの読み書きができるというのが特徴的で、プライマリキー(パーティションキーと、ソートキー※必須ではない)以外の項目はかなりの自由度の高さです。 まだ試していないですが、キー項目さえ存在すればレコードごとに全く異なるフォーマットのデータを登録できるのではないかと思います。 実際、テーブルを作成するときに、テーブル名とプライマリーキーを指定するだけで、その他の項目はレコード作成時に決めます。 下図がテーブル作成画面です。 テーブル作成に最低限必要な項目はテーブル名とパーティションキーの2つだけです。 (Auto Scaling の設定をする場合は別) これで、作成ボタンをクリックするとテーブルが完成します。 「項目」タブを選択して、「項目の作成」ボタンをクリックすると、下のような画面が表示されます。 ここで、レコードを登録します。 プライマリキーと、設定した場合はソートキーが必須のため初期表示されています。 +ボタンをクリックすることで項目の追加、削除が行えます。 使える変数は以下の通り。 スカラ型、オブジェクト型の2種類があります。 リレーショナルDBではないぶん、このオブジェクト型がめちゃくちゃ便利です。 Map型やList型はこんな感じで項目追加していくことができます。 下の例はMap型ですが、List型はキー値を持たない値の羅列となります。 Textを選択すると、JSON形式でデータを見ることができます。 これ、ここにJSON形式のテキストをコピペするとレコード登録できます。 すでにJSONファイルでデータがタンキングされている場合はデータベース化が容易に行えそうです。 簡単ですが、DynamoDBでのテーブル作成とレコード追加は以上となります。 結果的に、これら全てが必要なかったんですけどねorz 次回へ続く。 以下、ここまでの感想 リレーショナルではないため、正規化しようとすると使い勝手が非常に低下します。 そのため、極力一つのテーブルに全てを集約する非正規デザインになります。 ただ、JSONファイルのような感覚でデータベースを使用できるため、データベースと考えない方が違和感がなく使えるかもしれません。 ちなみに、地味にいらっとしたのがチュートリアル。 Musicっていうテーブルを作りましょう!って表示されるので素直にテーブル名に「Music」って入力すると「ミュージック」って入力しなさい、って怒られます。 そこはローカライズしなくていいじゃん!
2020.06.01
コメント(0)

テスト広告は表示されるのに、本物の広告ユニットIDを使用すると表示されないことが続いていた。AppStoreに配信してから日が浅いから、AdMob側に認識されていないのかな、なんてずっと考えていましたが、ふっとAdMobのマイページを見ていたら、「お支払い情報が登録されていないので、広告は表示されません」的なメッセージが載っていた。これか。。。お支払い情報を登録してしばらくしたら、ちゃんと表示されるようになっていました。↑ ワォ でた!!¥61の収益は私がテンションがあがってタップしたものと思われます笑ユーザー少ないので!!
2020.04.11
コメント(0)
朝会チャレンジの各ゲームの設定と結果を、アプリ内のDocumentsフォルダ内に、JSONファイル形式で保存している。構造体を使って、JSONファイルへの読み書きと、ゲーム内でのデータの編集・表示に使っているが、微妙にメンバ名に統一感がなくて気になっていたので、バージョンアップのついでにメンバ名を統一してみた。これでスッキリしたなーって思っていたが、実機に配布してみてびっくり。ゲームデータが全て初期化されている。理由もわからず気持ち悪いなぁ〜とか思いながら1日過ごしていたが、突然ひらめきました。ダンプしたわけじゃないので現時点で裏は取れていないのですが、おそらく変数名を変えたことが原因。先日、神ツールでJSONファイルから構造体を作ったとき、ファイル内のキー値が変数名になった。わかり易くするためかな?って思っていたのだけど、実は変数名とキー値が紐づいている?そう思って、変数名を全て修正前の状態に戻してみたけども、データ復活せず。しかーし、再度 変数名修正版に戻してみたらそちらのデータは残っていたようでちゃんと表示された。変数名を変えるとデータが取得できずに初期化される可能性大!変数名の設計もちゃんと事前にやる必要があるなーと痛感しました。
2020.04.09
コメント(0)
Dictionary型に、[id(連番の数値):値]という感じでデータを投入した。 入力時点では連番の昇順でデータを投入したので、その通り結果が帰ってくるのかと思いきや、実際に値を取得してみたらぐっちゃぐちゃ。 なんでかと思ったら、Dictionaryは順番を保証しないらしい。 というわけで明示的にソートして解決。 let dicSorted = dic.sorted{ $0.0 < $1.0 } 投入順序くらい保証してくれよ
2020.04.06
コメント(0)

神ツールにより生成された構造体。このままだと少々理解しづらかったので自分がわかりやすい形に変換しました。beforeafterもう2度とJSONの構造体を自分で書こうなんて思ったりしない、絶対に!順番が前後しましたが、JSONファイルそのものを作るときにもなサイトがありましたので紹介。https://jsoneditoronline.org/#right=local.yeragu&left=local.jizepe片側でプレーンテキストを記述して、もう片側で構造的にデータを見ることができるので、間違えがすぐにわかり易く便利です。(間違えてたらそもそも構造的に表示されない)FireFoxにJSONファイルをドロップしても同じように見れるんですが、編集は出来ないんですよね〜テキストエディタでJSONファイル作るのはもうやらない、絶対!!
2020.04.05
コメント(0)

朝会チャレンジにクイズ的なゲームを盛り込もうと思っています。クイズの質問、回答等はJSONでアプリ内に持たせようと思っていて、まずJSONファイルを作成しました。ところが、持ち前の頭の悪さにより、JSONファイルを構造体に起こせない。トライアンドエラーを繰り返した結果、JSONファイルから構造体作ってくれるツールとかないのかな、と思いググったらありました。あったんですよ。https://app.quicktype.ioJSON ファイルドラッグしたら終わりですよ。奥さん。世の中には神様のような人がいるんだな〜って思い知りました。
2020.04.05
コメント(0)

コンパイルしようと思ったら突如としてこんなコンパイルエラーが発生した。以下詳細Multiple commands produce 'ディレクトリ名/朝会チャレンジ.app/Info.plist':1) Target 'TMMiniGames' (project 'TMMiniGames') has copy command from 'ディレクトリ名/Info.plist' to 'ディレクトリ名/朝会チャレンジ.app/Info.plist'2) Target 'TMMiniGames' (project 'TMMiniGames') has process command with output 'ディレクトリ名/朝会チャレンジ.app/Info.plist'Info.plist が重複してますよエラー。何もしてませんが?解決策を探り、以下のようにして解決。念のため、事前にプロジェクトのバックアップを取得することをお勧めします!TARGETS → Build phases の Copy Bundle Resources から エラー対象のファイル(今回は Info.plist) を探してクリック。一番下まで移動して「-」ボタンをクリックして削除。消えたことの確認。これで無事コンパイルできるようになりました。たまーにこういう謎なのが急に発生するのが困ります。
2020.04.05
コメント(0)

朝会チャレンジに操作マニュアル欲しいな、と前から考えていたのですが、 HTMLで作って、アプリ内でロードすれば楽なんじゃない?と思い立ち実装しました。 早速実装。 HTMLと画像の配置はこんな感じ。 で、普通にHTMLを記述。 当然、画像のパスは./images/~ としたいじゃないですか。 したいですよね! でも、これだと画像のリンク切れ起こします。 なんでかな〜と思い、HTMLファイルと画像ファイルのパスをそれぞれダンプしてみました。 <HTMLファイルパスのダンプ> path String "/パス/朝会チャレンジ.app/00_Menu.html" <画像ファイルパスのダンプ>imgpath String "/パス/朝会チャレンジ.app/02-2_IM_game1.png" ダンプを取った断面が異なるので、「Application/~」のパスが少しことなていますが、そこは気にせず。 なんと、HTMLファイルと画像ファイルもアプリケーションのパッケージの直下にあります。 これは、、、汚い(階層的な意味で) ということで、以下の通りにすることで無事完了。 ./images/を削除しただけですね。 地味に時間取られました。 T-fal ティファール KO340177 電気ケトル JUSTINE+(ジャスティン プラス) サーブル [1.2L][ティファールケトル おしゃれ] 価格:3087円(税込、送料無料) (2020/4/4時点)楽天で購入
2020.04.04
コメント(0)
今週の火曜日くらいにAppStoreに申請したアプリの許可が降りて、無事にAppStoreで公開されました。先日ブログにアップしたプライバシーポリシー は申請に必要だったのですが、別に個人でWebサーバーを持っているわけでもなく、苦肉の策でブログにアップしてそのURLを載せたら審査が通りました笑ミニゲーム集のようなゲームですが、時間潰しにでもどうぞ。今後、アイデアがあれば少しずつゲームの種類を増やしていきます。心が折れるので、レビューに「クソゲー」と書かないでください笑申請するときにも紆余曲折あったので、整理してそのうちアップします。
2020.04.04
コメント(0)
1.情報を取得するアプリ提供者 たくぷれっさ2号機2.取得するユーザー情報と目的 A.ゲーム実行情報の記録 当アプリケーションでは、各ゲームの実行後に、実行者名、実行時間、実行結果を保存しますが、 あくまでゲームの挑戦履歴を保存することを目的としており、その他の目的では一切使用致しませ ん。 また、これらの情報はアプリケーション内のデータファイルの中に保存され、ご利用者の端末から 他の機器へ情報が送信されることはありません。 B.広告について 当アプリケーションでは、広告配信ツールとしてAdMob(Google Inc.)を使用しており、 AdMobがご利用者の情報を自動取得する場合があります。 取得する情報、利用目的、第三者への提供等につきましては、以下の広告配信事業者のアプリケー ション・プライバシーポリシーのリンクよりご確認ください。 Google 広告に関するポリシー https://policies.google.com/technologies/ads?hl=ja
2020.03.30
コメント(0)
![]()
ハマったことその2。これもAdMobと直接関係ないバグです。AdMobでは事前にアプリ情報を登録して、アプリIDと広告IDというやつを取得する必要があります。広告IDは、開発中はテスト用のIDを使用することが推奨されています。開発中も本番用のID使っていると、最悪アカウント停止になるようです。なので、プロジェクトのコンフィグレーションにより広告IDを動的に切り替えられるようにと思って下のようなコード書いたのですが、全く表示されない。---let configuration = Bundle.main.infoDictionary!["Configuration"] if(configuration as! String == "Debug") { self.bannerView.adUnitID = "ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx" } else if(configuration as! String == "Release") { self.bannerView.adUnitID = "ca-app-pub-yyyyyyyyyyyyyyyy/yyyyyyyyyy" }---おや?と思い、ブレークポイント置いてみたら、if にも else if にも引っかかっていない。info.plist から上手いことコンフィグレーションが取得できてなかったようです。で、下のように修正しました。--- #if DEBUGself.bannerView.adUnitID ="ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx" #elseself.bannerView.adUnitID ="ca-app-pub-yyyyyyyyyyyyyyyy/yyyyyyyyyy"#endif---無事完了。ただの無知です。macbook pro air 13 15 インチ ケース カバー macbookpro シェルケース macbookair パソコンケース PC保護ケース マックケース マックブック mac book マッキントッシュ アート デザイン Retina価格:4680円(税込、送料無料) (2020/3/30時点)楽天で購入
2020.03.29
コメント(0)
![]()
AppStoreへのアプリ配布を視野に、せっかくなのでバナー広告でも追加してみようかと思い、GoogleさんのAdMobを実装してみました。いろいろ躓いたんですが、その中で特にくだらない躓きから晒して行こうかと思います。AdMobはチュートリアルとサンプルコードが充実していて、ほぼコピペで済むくらい実装が楽です。サンプルコードが唯一頭を悩ませたのが、バナービューの追加。こちらがサンプル。---view.addSubview(bannerView)view.addConstraints( [NSLayoutConstraint(item: bannerView, attribute: .bottom, relatedBy: .equal, toItem: bottomLayoutGuide, attribute: .top, multiplier: 1, constant: 0), NSLayoutConstraint(item: bannerView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0) ])---ビューにバナービューに追加して、セーフエリアの最下段に追加していますが、Googleさんのサンプルコードが古いのか、BAD ACCESS(だったっけ?) になりました。んで、修正版。---bannerView.translatesAutoresizingMaskIntoConstraints = falsebannerView.translatesAutoresizingMaskIntoConstraints = falseself.view.addSubview(bannerView)bannerView.widthAnchor.constraint(equalToConstant: 320).isActive = truebannerView.heightAnchor.constraint(equalToConstant: 50).isActive = truebannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = truebannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true---無事成功。ビューの配置についてAdMobとは直接関係ありませんが、同じ罠にはまらないように。MacBook キーボードカバー 日本語 ( JIS配列 ) Air Pro Retina Pro13 Pro15 Touch Bar 11 12 13 15インチ Early 2015 2016 Apple Wireless Keyboard カバー《RMC 限定 オリジナル デザインカラー》 キーボード cover マック マックブック Mac iMac キーボードカバー価格:1329円(税込、送料別) (2020/3/30時点)楽天で購入
2020.03.29
コメント(0)

座標を扱う構造体のCGPoint。swift でこれ使いたくて変数定義をした。---var center : CGPoint!result = beforeresult.origin.x = center.x - (before.size.width / CGFloat(2))result.origin.y = center.y - (before.size.height / CGFloat(2))---こんな感じのコード書いたら、↓↓のような実行時エラーが発生しました。Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value要は NULL ッスよ、てことらしい。え、これって構造体じゃないの?と思いつつも変数定義を以下のように変更。---var center : CGPoint! = CGPoint(x:0.0,y:0.0)---無事解決。CGPoint ってポインター型なの?と思ってリファレンス開いたら疑惑はますます深まったと言える。Appleは説明責任を果たしてほしい。洗濯槽クリーナー 洗濯槽の激落ちくん 黒カビ洗浄 非塩素系 つけおき不要 ( 洗たく槽クリーナー レック 激落ちくん 激落ち 洗濯機 洗浄 洗剤 掃除用洗剤 掃除用具 )価格:258円(税込、送料別) (2020/3/25時点)楽天で購入
2020.03.25
コメント(0)
![]()
こんなコード書いたらコンパイル時にCPUが悲鳴をあげたというお話。個人的に好みではないけど、swift は型推論により変数型を指定しなくても、コンパイラ?側で代入された値によって自動的に判別してくれる。そこで厄介なのが、代入を行う際に左辺値と右辺値の型を全て統一する必要があること。要は、左辺値がDouble型なら、右辺値も、計算式の定数および変数も全部含めて全てDouble型に統一しないといけない。なので、型ごとに型変換する関数があり、それを使って統一していく。Double( 変数 or 定数 ) とかInt( 変数 or 定数 )的な感じ。で、viewの座標変換するために下のようなコードを書いたら、コンパイル時にファンが唸りはじめ10秒くらい経ってからエラーを吐くようになった。(エラーの詳細は忘れました(゚ω゚))---rect.origin.x = x_position + (64.0 * CGFloat(col)) +(10.0 * CGFloat(col))---↓のようなコードにすることで解決---rect.origin.x = x_position + CGFloat(64.0 * CGFloat(col)) + CGFloat(10.0 * CGFloat(col))---CGFloat型はその名の通りFloatなので定数値を 64.0 と書けばOKかな?って思ってたけど、ダメだった。Macが気の毒になるくらいコンパイル頑張ってたので、今後気をつけます。ちなみに 定数値を使う場合、 64 は Int型、64.0 はDouble型として判断されます。Floatを使いたい場合は、明示的に型を定義する必要があります。とここまで書いて、Double型(10.0)とCGFloat型の掛け算したからエラーを吐いたと気づくセンスも才能もないダメプログラマーです。あつまれ どうぶつの森価格:5920円(税込、送料無料) (2020/3/24時点)楽天で購入
2020.03.25
コメント(0)

一回書いた記事保存する前に閉じちゃった・・・Xcode 11.3.1 にアップデートしてからシュミレータでアプリを実行しようとすると、毎回下画像のエラーが出るようになった。要するに「アプリインストールできなかったけど、ごめんね(・ω<)」的なエラー。原因が不明なので根治できていないのですが、シュミレータ上のアプリを一回消して、コンパイル・実行すれば動くようになります。消したいアプリのアイコンを長押しして「Delete App」をクリック。これで動くようになりますが、次回実行にまた同じエラーが出ます。。。根本原因がわかったら記事を修正します。自分のためにね!!2020/4/5 追記なんか、これが解決してから発生しなくなった気がする。Multiple commands produceなんでなんだろう。ボールパークへ行こう!〜埼玉西武ライオンズ選手登場曲集2019〜 [ (スポーツ曲) ]価格:1936円(税込、送料無料) (2020/3/24時点)楽天で購入おすすめの一枚です。わかる人にはこれだけで私が楽天ゴールデンイーグルスが嫌いな理由がお察しいただけるかと思います笑
2020.03.24
コメント(0)

はじめまして。 たくぷれっさ2号機です。 なぜ2号機かというと「たくぷれっさ」という名前がすでに使われていたから。 今年の2月頃から数年ぶりにiOSアプリの開発を再開しました。 といっても、完全なる趣味の領域で、作って満足するだけなので特にAppStoreに配布などはしていません。 今後は配布してもいいかなって思えるものができたら配布していきます(Appleの審査が降りれば・・・)。 ということで、MacにインストールされていたXCodeのバージョンが古かったため、最新版のインストールから再開したわけですが、AppStoreでアップデートしようとしたところ、アイコンがクルクルしたままインストールが開始されない。 数時間放置していたら、インストール中の状態にはなったんだけども、これまた一向に進まない。 SSDはほぼ無音で高速といったメリットづくしのディスクですが、こいう時に動いているんだか動いてないんだかわからず困りますね😆 しびれをきらし、一旦 Mac を再起動し最初からやり直そうと思ったんですが結果変わらず。 長々経緯を描いても、後で自分が見た時にイラっとするので結論だけかくと、 https://developer.apple.com/download/more/ から、ダウンロードしました。 結局アーカイブの展開に2時間近くかかりましたが、進捗状況がわからないAppStoreよりは数倍ましです。 今後、XCodeのダウンロードにAppStoreはつかわねー MacOSのバージョンアップも同時進行したせいで、Time Machineのバックアップから始まり、足かけ2日、長い戦いだったよ。。。 【先着特典】侍の名のもとに 〜野球日本代表 侍ジャパンの800日〜 スペシャルボックス(ミニポスター)【Blu-ray】 [ 岸孝之 ]楽天で購入
2020.03.22
コメント(0)
全45件 (45件中 1-45件目)
1


