狛ログ

2019年12月26日木曜日

C#でExecuteScalarメソッドを使うときの注意点。


オフィス狛 技術部のHammarです。

C#のSQL実行メソッドに「ExecuteScalar」というのがあります。
これはクエリを実行し、そのクエリが返す結果セットの最初の行にある最初の列を返してくれます。
このメソッドを使って、ちょっとハマってしまったことについて書こうと思います。

例えば、テーブルの主キーとなるidが自動インクリメントになっているテーブルに、INSERTすると同時に登録したidを取得したい時などがあると思います。
今回試した環境はSQLServerだったので、INSERT実行時に
SELECT SCOPE_IDENTITY();
を使えばそれが可能なのですが、INSERT文が別テーブルからの「INSERT SELECT」の場合、SELECTの結果が条件によっては取得できない場合があります。
そのときの結果の挙動で自分が想定した結果ではなかったので、ちょっと戸惑いました。

まず、上記の「INSERT SELECT」と「SELECT SCOPE_IDENTITY」を使って「ExecuteScalar」を実行するサンプルコードとして

SqlConnection Connection = new SqlConnection();
SqlCommand cmd = new SqlCommand("INSERT INTO users(...) SELECT ...; SELECT SCOPE_IDENTITY();", Connection);
decimal id = (decimal)cmd.ExecuteScalar();

上記は、別テーブルから取得した情報を登録し、登録した時のidを取得するというような処理になります。

で、基本SELECTでデータが取得できる前提であれば問題ないんですが、このSELECTでデータが取得できない場合、
「指定されたキャストは有効ではありません。」
というエラーが発生してしまいます。

恐らくこのエラーは、「SELECTでデータ取得できない」→「INSERTするデータなし」→「SELECT SCOPE_IDENTITYでid取得できない」
ということなので、返り値としてはNullが返ってくると思います。
なので、結果decimalにキャストできなくてエラー発生していると思ったので、以下のように、Nullが入ってくることを許容すればいいと思い、返り値にNullable型を使って

SqlConnection Connection = new SqlConnection();
SqlCommand cmd = new SqlCommand("INSERT INTO users(...) SELECT ...; SELECT SCOPE_IDENTITY();", Connection);
decimal? id = (decimal?)cmd.ExecuteScalar();
if (id.HasValue)
{
    string insertId = id.ToString();
}

という感じにしました。
返り値の型decimalに?を付けてNull許容(Nullable)とすれば、仮にNullが返っても問題ないのかなと。
その後で、Nullの場合の判定を加えてやれば処理的にうまくいくかなと思いましたが、やはり同じく「指定されたキャストは有効ではありません。」のエラーが発生しました。

よくよく考えてみると、そもそもここでdecimal型にキャストして、基本的に値が返ってくる前提のような作りがよくないのと、SELECT時データが取得できない場合は、返り値が「Null」ではなくて、「DBNull」という別物だということを知りました。

ということで、返り値の型をobject型に変更し、下記のように変更しました。

SqlConnection Connection = new SqlConnection();
SqlCommand cmd = new SqlCommand("INSERT INTO users(...) SELECT ...; SELECT SCOPE_IDENTITY();", Connection);
object id = cmd.ExecuteScalar();
if (!DBNull.Value.Equals(id))
{
    string insertId = id.ToString();
}

これでデータが取得できた時だけ、登録時のidを取得できるようになりました。

もしかしたらこういったケースはあまりないかもしれませんが、「ExecuteScalar」メソッドを使って「INSERT SELECT」するときには、ご参考にしてみてください。

2019年12月18日水曜日

zshでプロンプトをコマンドの実行結果によって変更する方法。


オフィス狛 技術部のmmm(むー)です。

TL; DR

zsh(oh-my-zsh)をインストールして、下記のようにプロンプト(➜ 😶/ ➜ 🙃🙃🙃)をコマンドの実行結果によって変更する設定を説明します。さよなら、bash。
※特に意味はありませんが、あえて良い点をあげるとローカルとリモートの区別が簡単につきます。
【環境】
MacOS Mojava
バージョン 10.14.5

【ターミナルアプリ】
iTerm2
https://www.iterm2.com/
※これは好みの問題なので、Macのデフォルトのターミナルでも問題ありません。

1. Homebrewのインストール

Homebrewをインストールします。
(HomebrewとはMac専用のパッケージマネージャーで、インストールしておくと便利です。)
➜ 😶/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

インストールできていることの確認
➜ 😶brew -v
Homebrew 2.2.1
Homebrew/homebrew-core (git revision 0105; last commit 2019-12-10)

参考)Homebrew
https://brew.sh/index_ja

2.zshのインストール

zshをインストールします。
➜ 😶brew install zsh zsh-completions

インストールできていることの確認
➜ 😶zsh --version
zsh --version
zsh 5.7.1 (x86_64-apple-darwin18.2.0)

3.oh-my-zshのインストール

oh-my-zshとは、zshのフレームワークです。
➜ 😶sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

参考)of-my-zsh
https://github.com/ohmyzsh/ohmyzsh

oh-my-zshにはたくさんのカラーテーマが用意されています。
テーマ等修正したい場合、~/.zshrcを修正すると適用されます。
robbyrussellはデフォルトのテーマで私はデフォルトのままにしています。
➜ 😶vi ~/.zshrc

~省略~
# Set name of the theme to load --- if set to "random", it will
# load a random theme each time oh-my-zsh is loaded, in which case,
# to know which specific one was loaded, run: echo $RANDOM_THEME
# See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes
ZSH_THEME="robbyrussell" #ここを変更する
~省略~

参考)oh-my-zshカラーテーマ一覧
https://github.com/ohmyzsh/ohmyzsh/wiki/Themes

4.プロンプトの修正

現在使用しているzshのテーマを確認。
先ほど変更したものは変更したものに、変更していない人はrobbyrussellが表示されます。
➜ 😶echo $ZSH_THEME
 robbyrussell

自分が使用しているカラーテーマの対象のファイルを修正します。
※変更後は適用を反映するため、ターミナルを起動しなおしましょう。
➜ 😶vi ~/.oh-my-zsh/themes/robbyrussell.zsh-theme

 PROMPT="%(?:%{$fg_bold[green]%}➜ 😶:%{$fg_bold[red]%}➜ 🙃🙃🙃 )"  #ここが基本のプロンプト
 PROMPT+=' %{$fg[cyan]%}%c%{$reset_color%} $(git_prompt_info)'
 
 ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[blue]%}git:(%{$fg[red]%}"
 ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} "
 ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}) %{$fg[yellow]%}✗"
 ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%})"

🍭~/.oh-my-zsh/themes/robbyrussell.zsh-themeファイルの設定の補足
※ここは読まなくても、設定は完了しています。

%(?:%{$fg_bold[green]%}➜ 😶:%{$fg_bold[red]%}➜ 🙃🙃🙃 )」の部分ですが
 %(?:true:false)なので、下記のようになります。

成功した場合「%{$fg_bold[green]%}➜ 😶
echo $?の結果が0 (直前の処理が成功)

失敗した場合「%{$fg_bold[red]%}➜ 🙃🙃🙃
false:echo $?の結果が0以外 (直前の処理が失敗)

$?で終了ステータス(直前のコマンド実行結果)を取得することができます。
0が成功。1は失敗の意味です。
(コマンドによっては、上記ルールに適していないものもあります。例えば、diffは差分がない場合0、差分が有れば1、失敗すると2です...)
# 直前の処理が成功
➜ 😶ls test.txt
test.txt
➜ 😶echo $?
0

# 直前の処理が失敗
➜ 😶ls a
ls: a: No such file or directory
➜ 🙃🙃🙃echo $?
1

残りの箇所ですが、文字の色を変更しています。
 %{$fg_bold[green]%}:文字の色を緑に
 %{$fg_bold[red]%}:文字の色を赤に

最後の補足が長くなってしまいましたが、以上となります。
オリジナルのプロンプトをぜひ作成してみてください。

,

2019年12月16日月曜日

Angular で作られたプロジェクトをPWAにしてみよう。(後編)正しくPWA化されているか確認する


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

前編(Angular 7 から 8 へのアップデート)中編(PWA化の実施)の続きです。
今回は、PWA化したプロジェクトが、正しくPWA化されているか確認しましょう。

(1)前提条件

・ビルドはproduction設定の「ng build --prod」で行う。
・サイトへの接続はHTTPSであること。

最初のproduction設定については、後でいくらでも変更は出来るのですが、デフォルトだと「serviceWorker」がproductionの場合のみ有効になるので必須となります。
2番目の「HTTPS」ですが、チェックツールの「Chrome デベロッパーツールのAudits」がHTTPS接続でないと、そもそも使えないので、これも致し方ないです。

(2)PWAかどうかチェックする

さて、早速チェックしていきます。Chromeで対象のサイトを表示し、デベロッパーツールを起動します。(右クリックから「検証」を選ぶと早いです。)
デベロッパーツールが起動したら、Auditsと言うタブがあるので、そのタブを選択します。


続いて、Auditsの設定を行います。


今回は、PWAのチェックだけ実施したいので、他のチェックは外します。

それでは実行してみましょう!


???
チェック出来てない・・・。
あ、なるほど。
このプロジェクトは、AWSのEC2に配置し、ALB(ロードバランサ)でバランシングしているのですが、
ロードバランサ側でhttp接続は処理しないようになっていました。
と言う事で、設定を変更し、HTTPはHTTPSにリダイレクトするようにしました。

気を取り直して、もう一度、チェック実行してみます。


とりあえず、「?」マークはなくなりましたね。
後は、
Does not have a <meta name="viewport"> tag with width or initial-scaleNo viewport meta tag found
Add a viewport meta tag to optimize your app for mobile screens.
ですね。
こちらは、エラー内容の通り、metaタグ(viewport)が無い事で発生しています。

そして、もう一つ
Does not provide a valid apple-touch-icon
For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG.
こちらはiPhone・iPadのホーム画面用アイコンapple-touch-iconnの指定がありませんよ、と言う事で発生しています。

いずれも、「index.html」に記述する事で解決しそうです。
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="manifest" href="manifest.webmanifest">
  <meta name="theme-color" content="#1976d2">
  <meta name=”viewport” content=”width=device-width,initial-scale=1.0,minimum-scale=1.0″><!-- ←追加 -->
  <link rel="apple-touch-icon" href="assets/icons/icon-192x192.png"><!-- ←追加 -->

Angular CLIで、ここまでやってくれると嬉しいですけどね。

という事で、再度、チェック実行してみます。


無事にPWAにチェックマーク✅が付きました!

と言う事で、三回に渡って、Angular で作られたプロジェクトをPWAにしてみました。
途中でAngularアップデートをやっていたので、時間が掛かってしまいましたが、
PWA化自体は非常にお手軽に出来たので、かなり良い感じだと思います。

では、より良いAngularライフを!


,

Angular で作られたプロジェクトをPWAにしてみよう。(中編)PWA化の実施


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

前編(Angular 7 から 8 へのアップデート)の続きです。
今回は、実際にPWA化を行って行きます。
既にPWA化作業が終わっていて、正しくPWA化されているかを確認したい方は、
後編(正しくPWA化されているか確認する)をご覧ください。

では、早速PWA化を行って行きましょう。

(1)PWAパッケージの追加

前編で、Angular8にアップデートしたプロジェクトを使用します。
そのプロジェクトにPWAパッケージを追加します。

追加のコマンドは「ng add @angular/pwa」です。
$ng add @angular/pwa
+ @angular/pwa@0.803.5
added 5 packages from 2 contributors and audited 22482 packages in 22.952s
Installed packages for tooling via npm.
Could not read file (/src/@app/app.module.ts).
エラーになりました。「app.module.ts」が読めないと言っていますね・・・。

「app.module.ts」をimportしているのは、確か「main.ts」なので、そちらを見てみましょう。
import { AppModule } from '@app/app.module';
「@app」になっているのが駄目なのでしょうね。
(でも、元々これはAngular CLIが勝手に作ったような・・・)

とりあえず、
import { AppModule } from './app/app.module';
に修正しました。

では、再度、「ng add @angular/pwa」を実行します。
$ng add @angular/pwa
CREATE ngsw-config.json (620 bytes)
CREATE src/manifest.webmanifest (1119 bytes)
CREATE src/assets/icons/icon-128x128.png (1253 bytes)
CREATE src/assets/icons/icon-144x144.png (1394 bytes)
CREATE src/assets/icons/icon-152x152.png (1427 bytes)
CREATE src/assets/icons/icon-192x192.png (1790 bytes)
CREATE src/assets/icons/icon-384x384.png (3557 bytes)
CREATE src/assets/icons/icon-512x512.png (5008 bytes)
CREATE src/assets/icons/icon-72x72.png (792 bytes)
CREATE src/assets/icons/icon-96x96.png (958 bytes)
UPDATE angular.json (5936 bytes)
UPDATE package.json (2638 bytes)
UPDATE src/app/app.module.ts (1597 bytes)
UPDATE src/index.html (542 bytes)
無事に完了したようですね。(*1)
*1)警告が出る場合、「npm audit fix」しておきましょう

では、自動作成・更新されたファイルについて、エラーが出ていないか確認しておきましょう。

私が試したプロジェクトだと、「app.module.ts」がエラーになっていました。
import { environment } from '@environment/environment'; // ←元々

import { ServiceWorkerModule } from '@angular/service-worker'; // ←追加された行
import { environment } from '../environments/environment'; // ←追加された行

元々の表記が「@environment」だったので、別パスと判断して、新たな「environment」を追加したのですかね。
(でも、元々これはAngular CLIが勝手に作ったような・・・(2回目))

まあ、ここは、追加された行を残して、元々の行を削除しようと思います。

(2)App Shellの導入

続いて、App Shellを導入します。
App Shellは、簡単言うと、画面のキャッシュを行う機構の一つです。
詳細は、この辺を参照してください。

Angularに導入するには、以下のコマンドを実行します。
ng generate app-shell --client-project my-app --universal-project server-app
・my-app はクライアントアプリケーションの名前です。
・server-app はユニバーサル (またはサーバー) アプリケーションの名前です。

と言う事で、今回は、以下のコマンドを実行します。
ng generate app-shell --client-project koma-system-pwa --universal-project koma-system-pwa
エラーになりました。
Could not find /src/app/@app/core/containers/app/app.component.ts.
先程までと同じ、「@app」問題ですね。
(でも、元々これはAngular CLIが勝手に作ったような・・・(3回目))

先程と同じように、
import { AppComponent } from '@app/core/containers/app/app.component';
上記を下記のように書き換えます。
import { AppComponent } from './core/containers/app/app.component';
弊社のプロジェクトの場合、ちょっと特殊な場所に「app.component」を配置しているので、それぞれ環境にあったパスに修正してください。

それでは、再度、実行してみます。
$ng generate app-shell --client-project koma-system-pwa --universal-project koma-system-pwa
CREATE src/main.server.ts (220 bytes)
CREATE src/app/app.server.module.ts (590 bytes)
CREATE src/tsconfig.server.json (270 bytes)
CREATE src/app/app-shell/app-shell.component.css (0 bytes)
CREATE src/app/app-shell/app-shell.component.html (24 bytes)
CREATE src/app/app-shell/app-shell.component.spec.ts (643 bytes)
CREATE src/app/app-shell/app-shell.component.ts (280 bytes)
UPDATE package.json (2679 bytes)
UPDATE angular.json (7231 bytes)
UPDATE src/main.ts (469 bytes)
UPDATE src/app/app.module.ts (1650 bytes)
これでPWA化は完了となります。簡単ですね。

後は、自動生成された画像(src/assets/icons)をそれぞれのプロジェクト用で置き換えてください。

では、次回で最後、Chrome デベロッパーツールのAuditsで、正しくPWA化されている事を確認するところまでやって行きます

次回:Angular で作られたプロジェクトをPWAにしてみよう。(後編)正しくPWA化されているか確認する

,

Angular で作られたプロジェクトをPWAにしてみよう。(前編)Angular 7 から 8 へのアップデート


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

すっかりBlogをサボってしまいました。
社内勉強会用の資料作成で、Android&Kotlinにどっぷり浸かっていました。(言い訳)
気を取り直して、今回は久しぶりのAngularです。

弊社、iOSもAndoridもネイティブでゴリゴリ開発しているのですが、実は、プロジェクト的には減少気味です。
やっぱりアプリの申請とか、色々と気にする事が多いのでしょうね。
代わりにSPAのサイトをレスポンシブで作るプロジェクトがかなり増えています。
そんな中、新たな選択肢として、PWAのプロジェクトも増えています。

と言う事で、今回は、「既存のAngularプロジェクトをPWA化する」を備忘録的に記事にします。

【注意】当記事では、事前準備までを説明していて、実際にPWA化までは行っていません。PWA化のみ見たい方は、「Angular で作られたプロジェクトをPWAにしてみよう。(中編)PWA化の実施」をご覧ください
Angular で作られたプロジェクトをPWAにしてみよう。(中編)PWA化の実施

(1)対象のシステム要件

今回やろうとしていることは以下の通りです。
・既存のAngularで作られたSPAサイトをPWA用として新たに作成する。(既存のサイトは残す。つまりコピーしてサイト名変更)
・既存のサイトのAngularバージョンは「7」
・最終的には、Chrome デベロッパーツールのAuditsで、正しくPWA化されている事を確認する。
・その他の機能についてのアップデートは行わない(もっとも少ない対応内容でPWAを実現させる)
では、早速、やって行きましょう!

(2)サイト名(プロジェクト名)変更

まずは、既存のサイト(koma-system-web)をコピーして、名称を「koma-system-pwa」に変更します。
サイトのrootディレクトリ名称変更の他に、以下のファイルにもサイト名が記載されているので、合わせて修正します。
angular.json
app.e2e-spec.ts
package.json

(3)Angular 7 から 8 へのアップデート

Angular 8から、CLIによるPWA作成が簡単に出来るようになっているので、まずはAngularのバージョンアップを行います。
基本的な作業としては、こちらの手順通りになります。


バージョンは(Angular Version)は、7.0から8.0でいきます。
アプリの複雑度(App Complexity)は・・・・Mediumぐらいですかね、とりあえず。
その他の依存関係(Other Dependencies)ですが、今回のプロジェクトは、AngularJSも混在していませんし、 Angular Material、Express、Hapiも使用していないので、チェックは付けません。
(Express、Hapiは、サーバーサイドレンダリングで使用するものですかね、多分)
パッケージ管理(Package Manager)はnpmを使っているので、npmを選択します。

【更新する前に(Before Updating)】

サイトに記載している手順と注意書き通りに作業を進めて行きます。
If you use the legacy HttpModule and the Http service, switch to HttpClientModule and the HttpClient service. HttpClient simplifies the default ergonomics (you don't need to map to JSON anymore) and now supports typed return values and interceptors. Read more on angular.io.
HttpModuleは使えないからHttpClientModuleに変更してね、と言う事ですね。
HttpClientModuleは、確かAngular 4.3 ぐらいから使えるようになった機能です。
Once you and all of your dependencies have updated to RxJS 6, remove rxjs-compat.
rxjs-compatを使っているなら、削除してね、と言う事ですね。
If you use the Angular Service worker, migrate any versionedFiles to the files array. The behavior is the same.
Angular Service workerを使っている場合もマイグレーションを行う必要があるようですが、
今回のプロジェクトでは使用していないので、特に何もしません。

【更新作業(During the Update)】

Update to version 8 of the core framework and CLI by running ng update @angular/cli @angular/core in your terminal
早速実行していきます
無事に実行できたら(*1)、プロジェクト内を確認してみましょう。
*1)ちなみに、私が試したプロジェクトでは、いくつかのプラグインがAngular8に対応していませんでしたので、代替のプラグインを取り込んだりして、何度もやり直しました。
Angular now uses TypeScript 3.4, read more about errors that might arise from improved type checking.
あれ、アップデート後のpackage.jsonを見ると、typescriptが3.5.3にupdateされているけど、良いのかな・・・?
Make sure you are using Node 10 or later.
こちらも念の為確認
$node --version
v10.14.1
うん、大丈夫そう。

【更新後作業(After the Update)】

では、最後に、「ng update」を実行しますが、
その前にちょっと試しに「ng update --all」をやってみました。
すると・・・・
Package "@angular-devkit/build-angular" has an incompatible peer dependency to "typescript" (requires ">=3.1 < 3.6", would install "3.6.3")
Package "@angular/compiler-cli" has an incompatible peer dependency to "typescript" (requires ">=3.4 <3.6", would install "3.6.3").
Package "@angular/core" has an incompatible peer dependency to "zone.js" (requires "~0.9.1", would install "0.10.2").
Incompatible peer dependencies found.
Peer dependency warnings when installing dependencies means that those dependencies might not work correctly together.
You can use the '--force' option to ignore incompatible peer dependencies and instead address these warnings later.
色々出てますが、TypeScriptのバージョンで怒られているものはないですね。
3.5.3は大丈夫なんだ・・・。

と言う事で、地道に「ng update」で更新が必要なものを探ります。
$ ng update
Using package manager: 'npm'
Collecting installed dependencies...
Found 54 dependencies.
    We analyzed your package.json, there are some packages to update:
    
      Name                               Version                  Command to update
     --------------------------------------------------------------------------------
      @ngrx/store                        7.4.0 -> 8.3.0           ng update @ngrx/store

と言う事なので、ひとまず、「ng update @ngrx/store」でngrxだけ上げます。

これで、「ng build」してみると・・・・

An unhandled exception occurred: Could not find the implementation for builder @angular-devkit/build-angular:dev-server
エラーになってしまいました。
と言う事で、「ng update @angular-devkit/build-angular」も実行します。

そして、最後の最後に「npm audit fix」で、脆弱性が発生しているパッケージなどのチェックと修正を同時に行ってしまいます。

これで、「Angular 7 から 8 へのアップデート」は完了です。

と言う事で、すいません、長くなってしまったので、続きは中編に記載します。
(同時に記事公開しますので、許してください)

次回:Angular で作られたプロジェクトをPWAにしてみよう。(中編)PWA化の実施

,

2019年12月9日月曜日

PHP Conference 2019に行ってきました。


オフィス狛 技術部のmmm(むー)です。
先週のことですが、PHP Conference Japan 2019に行ってきました。
(現在の業務ではPHPは使用しておりませんが…)

・ 公式URL
https://phpcon.php.gr.jp/2019/
・ セッションのタイムテーブル
https://fortee.jp/phpcon-2019/timetable

今年のテーマは 「beyond .*」で、これから先のPHPの未来がさらに飛躍するようにとつけたようです。
今回第20回目を記念して、PHPの像のぬいぐるみが販売されていました。

自由すぎると定評のPHPですが、先月末7.4がリリースされました。
オープイングセッションでは、PHP7.4の新機能の紹介がありまして、型付けプロパティやアロー関数を使用できるようになりました。
次にリリースされるバージョンは8になるようです。
また、5.6も7.1もEOL(End of Life)になったねって話が出てました。あっという間ですね…

カンファレンスに参加する醍醐味は、自分が知らない技術・考え・作者の方の思いを知ることができることだと思います。以下、各セッションの感想になります。

MVCにおける「モデル」とはなにか

天重誠二さん
https://speakerdeck.com/tenjuu99/what-mvc-is

フレームワークの話をするのかと思っていましたが、概念的な話でした。
ドメインモデルについての話を現実世界で使用している伝票システムにたとえて説明をしていたのが面白かったです。
また、誰にとってのデザインかを考えた上で設計するのは大切ですね。
今回の発表でたくさんの参考文献が紹介されていたのですが、その中の一つ「誰のためのデザイン」は私も以前プログラマーでも読んだ方が良いとデザイナーの方に勧められて読みました。
タイトルの通り誰のためにデザイン(設計)するのかということが、実生活を例に紹介されていてとても面白い本でした。他の参考文献も読んでみたいと思います。

思想と理想の果てに -- クリーンアーキテクチャのWEBフレームワークを作ろう

成瀬允宣さん
https://nrslib.com/phpcon-2019-proposal/

クリーンアーキテクチャとはフレームワークに依存しないという前提のもと、環境に依存しないフレームワークを作る説明のセッション。クリーンアーキテクチャを採用すれば、どの処理をそこに書いたか悩むことから解放されます。
この発表を他の会場で行った時、似たようなライブラリがあると参加者の方に聞けたので、アウトプットする人のところに情報が集まってくる(アウトプットしよう)とのことでした。
また、フレームワークは変わらないと思っていても、10年15年やってるとフレームワーク変えることはあり、10年後保守してる人達が辛い思いをするからフレームワーク非依存をおすすめしますといった内容でした。

PHPは何を捨て、どんな力を手に入れてきたのか

うさみけんたさん
https://www.pixiv.net/fanbox/creator/105589/post/691574

PHPを使用していると、PHP5.3を最後にPHP6がスキップされて、次は7になっていることは一目瞭然だと思います。
PHP5.3は、6が開発されるまでの繋ぎで、6でUnicode化等を行うつもりでしたが 開発が止まってしまったのでした…。
現在、他のモダンと言われている言語に追いつくためにPHPは歴史的経緯で残っているレガシーコードを消していこうという動きがあります。
は使用できないよう、PHPから派生したP++を作る…等、今年の夏頃議論が白熱してPHP界隈で炎上していました。
銀河の平和はまだわかりませんが、詳細は調べてみてくださいとのことでした。

PHPUnit: Past, Present and Future

Sebastian Bergmannさん
https://thephp.cc/dates/2019/12/php-conference-japan/phpunit-past-present-future

PHP Unitの開発者の方のセッションです。別のセッションの関係で、途中から聞きました。
Sebastianさんの誕生やプログラミングを始めたこと、PHPが誕生したこと、PHP Unitの開発を始めたこと順を追って各歴史を順番に振り返えるセッションでした。 PHP4では例外処理がなかったため大変だった等の話を聞いて、私にとってはあって当たり前の機能だったので、その時代の話が聞けて面白かったです。
セッション終了後には、エモかったという感想がTwitterにあふれていました。
ちなみに、PHP Unitのロゴは好きなゲーム、宇野デザイナーさんにお願いしたそうです。
英語でしたが、ゆっくり話してくださっていたので聞きやすかったです。

How to Supercharge your PHP Web API

Aurimas Niekisさん
※スライドがアップされていませんでした

私のプログラマーとしてのスキル能力と英語力が足りなくて、内容がわかりませんでした…
セッションが終了した後に、少し対面で話をさせていただいたのですが、Aurimasさんは来年の2月から日本で働き始めるようです。
また、私がNode.jsを今業務で使用していることを伝えると、Node.jsも使っているので質問があればいつでも連絡してねと言ってくださいました。(優しい…)


カンファレンスに参加していると知り合いが少しずつ増えていくので、時々集まる同窓会のようで楽しいです。
来年のPHP Conferenceは2020/10/11に開催されるようですので、都合が合えば参加したいです。


,

2019年11月30日土曜日

knex.jsで実行したSQL文を確認する。

オフィス狛 技術部のHammarです。

以前の投稿で、knex.jsのtipsをいくつか書いたりNode.jsでknex.jsを使ったSQL書き方Tips、弊社技術部のmmm(むー)が書いたりしていますが(knex.jsでfromに2つ以上のサブクエリを書く方法)、今回はもう1つ「実行したSQL文を確認する」やり方を書きたいと思います。

これ意外とよく使うのに、やり方がぱっとわからなくて、社内でもどうやるのか質問があったりしたので新たにtipsとして追加したいと思います。

もちろん公式サイトにも載ってはいるのですが、日本語対応されていないのでちょっとわかりずらいんですよね。
で、やり方は単純で、SQL実行文に.toSQL()という記述を追加することで確認可能です。

具体的には

■記述例1

knex
  .select('*').from('users')
  .where('id = ?', [1]))
  .toSQL()

上記の結果を出力すると、
bindings: [1],
method: 'select',
sql: 'select * from "users" where id = ?',
options: undefined,
のように出力されて、バインド値と実際のSQL文を確認することができます。

ちなみにpostgresqlはSQL実行文に.toSQL().toNative()と記述することで同様の結果が得られるようです。

■記述例2

knex
  .select('*').from('users')
  .where(knex.raw('id = ?', [1]))
  .toSQL().toNative()

出力結果
bindings: [1],
sql: 'select * from "users" where id = $1',

自分も最初はこのやり方を知らずに、デバッグでステップ実行してどこでSQL作っているのかずーっと奥の方まで追って行って、確認するのに結構苦労してました(苦笑)

以上のように知ってしまえば簡単なんですが、知らないとかなりやりずらいので、ご参考にしてみてください。

,

SpringBoot「1.5」を「2.1」へバージョンアップ。


オフィス狛 技術部のJoeです。

SpringBoot 1系が2019/8/1にEOLになり、担当しているプロジェクトもバージョンアップ要件(1.5 → 2.1)がありました。
バージョンアップで対応した内容の一部をご紹介します。

① application.ymlファイルのエラー

非推奨となったプロパティが大量に出てきました。こちらは全てメッセージに従いキー名を変更することで解決しました。

【参考)メッセージ】
Property 'spring.thymeleaf.content-type' is Deprecated: Use 'spring.thymeleaf.servlet.content-type' instead.
Property 'server.session.timeout' is Deprecated: Use 'server.servlet.session.timeout' instead.
Property 'spring.messages.cache-seconds' is Deprecated: Use 'spring.messages.cache-duration' instead.
Property 'spring.http.multipart.max-file-size' is Deprecated: Use 'spring.servlet.multipart.max-file-size' instead.
Property 'spring.http.multipart.max-request-size' is Deprecated: Use 'spring.servlet.multipart.max-request-size' instead.
Property 'spring.datasource.initialize' is Deprecated: Use 'spring.datasource.initialization-mode' instead.

② Beanオーバーライドが無効

次に下記エラーが発生しました。
The bean 'XXX', defined in class path resource [XXX.class], could not be registered. A bean with that name has already been defined in class path resource [XXX.class] and overriding is disabled.

SpringBoot 2系では、Beanオーバーライドがデフォルトで無効になったようです。
メッセージのActionに従い、application.ymlファイルに下記を追記しました。
spring.main.allow-bean-definition-overriding: true

③ 非推奨、廃止、構成変更のパッケージ

非推奨、廃止、構成変更になったパッケージがいくつかありましたので、下記のように修正しました。

■非推奨
【変更前】
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
【変更後】
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;

■廃止
【変更前】
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
public class MessageConfig extends WebMvcConfigurerAdapter { ・・・
【変更後】
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class MessageConfig implements WebMvcConfigurer { ・・・

■構成変更
【変更前】
import org.springframework.boot.web.support.SpringBootServletInitializer;
【変更後】
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

④ application.ymlファイルから@ConfigurationPropertiesで値が設定されない

こちらは動作確認をして気が付いたのですが、@ConfigurationPropertiesアノテーションを付けたクラスでsetterメソッドがstaticだと、application.ymlファイルから値を設定できなくなってしまいました。
下記のように非staticなメソッドに変更することで無事設定されました。
@Component
@ConfigurationProperties(prefix = "outerconf")
public class OuterConf {

  private static String rootdir;

  public static String getRootdir() {
    return rootdir;
  }

  // 非staticに変更
  public void setRootdir(String rootdir) {
    OuterConf.rootdir = rootdir;
  }
}


SpringBootのバージョンアップはたくさんの方が記事にされていますが、環境によって対応が異なるとは思いますので、何かお役に立てれば幸いです。

,

2019年11月29日金曜日

「コンテンツに応じた塗りつぶし」で写真の不要なものを消す。


こんにちは、オフィス狛 デザイン部のSatoです。

今回はPhotoshopCC2019から機能が強化された「コンテンツに応じた塗りつぶし」という機能を使って写真から不要なものを消す方法をご紹介しようと思います。
この機能を使うとあのAdobe Senseiが数分で遠目で見る分には問題ないくらいに自然に選択した部分の中身を近くの部分そっくりにしてくれます。

https://unsplash.com/photos/Osd4ngHD4kM
↑今回はこの写真から中央に立っている人物を消してみます。
とっても綺麗な写真ですが今は無人の廃墟の写真が欲しいので女の子にはどいてもらいましょう……。


まず、消したいものを選択していきます。
精密に消したい方は自動選択ツールなどがいいかと思いますが、今回はなげなわツールでざっくり選択しました。


選択後、Photoshopメニューバーの「編集」の「コンテンツに応じた塗りつぶし」をクリックします。


コンテンツに応じた塗りつぶしツールのワークスペースが表示されます。
左が編集画面、右がプレビュー画面です。


緑になっている部分はサンプリングされる(人工知能が参考にする)部分です。
右上のサンプリングブラシツールを使って緑の部分を微調整していきます。
サンプリング箇所の拡張と削除のブラシの切り替えは上部のサンプリングブラシツールのオプションの+と-のアイコンでも切り替えできます。またOptionかAltキーを押しっぱなしにしても切り替えができます。

消したい選択範囲を広げたい場合なげわツールで追加しましょう。


プレビュー画面で確認しつつ、いい感じになったら右下にあるOKボタンを押すと通常のワークスペース画面に戻り、(右にある出力先設定の値が初期設定の「新規レイヤー」になっている場合)修正した部分の新しいレイヤーができています。


違和感があまりなく人物が消えましたね!
しかし拡大しよく見ると、木と石が合体したような瓦礫などができあがってしまっていたり少し違和感があります。
拡大すると意外と気になりますね…。
このような気になる部分はコピースタンプツールで微調整をします。
全体的に見ると少し違いが分かりづらいので比較画像にまとめてみました。

この方法で修正した後の画像がこちらです。
遠目だと分かりずらいですが、比較画像の通り変になっている部分と人物の影をコピースタンプツールで消しました。
拡大しても大丈夫なくらいに違和感が減りました!
「コンテンツに応じた塗りつぶし」のみで完璧なものができる訳ではなさそうですが、コピースタンプツールやパッチツールで修正するよりもかなり手早く「遠目で見ると違和感ないレベルまで」修正できて感激です。

他の空の写真と組み合わせて夕暮れの写真を作ってみました。
この写真の空の部分のみトリミングし使いました。
色々と細かい調整が大変で人物を消す+微調整の10倍以上の時間がかかってしまいました。
つまり「コンテンツに応じた塗りつぶし機能」は本当に素早く人物を消すことができます!
コピースタンプツールで人や邪魔なものをチマチマ……消さなくていいのは本当にありがたいですね……。

Adobeのマニュアルに「コンテンツに応じた塗りつぶし」機能の詳しい使い方が書いてあるのでそちらもチェックしてみてください。


今回のブログ記事に使用した写真は全てUnsplashというフォトストックサイトのものを使わせていただきました。
Unsplashに掲載されている写真は無料かつクレジット不要で写真加工や商業利用可なのでデザイナー的に非常にありがたいサイトです。
More precisely, Unsplash grants you an irrevocable, nonexclusive, worldwide copyright license to
download, copy, modify, distribute, perform, and use photos from Unsplash for free, including for
commercial purposes, without permission from or attributing the photographer or Unsplash.
This license does not include the right to compile photos from Unsplash to replicate a similar or competing service.

Licenseページより引用
上記の通り利用規約が非常に緩い+Unsplash APIというなにやら便利なものもあるらしいのでTrelloなどのwebサービスも写真をよく使っているようです。
今回使用した写真含め、とても綺麗な写真が多いので見ているだけでも楽しいサイトですので、ぜひ活用してみてください。

2019年11月28日木曜日

knex.jsでfromに2つ以上のサブクエリを書く方法。


オフィス狛 技術部のmmm(むー)です。
今回初投稿になります。
業務ではNode.jsを使用してAPI作成をしています。

今回は、Node.jsでDB操作を行う為のknex.jsと言うクエリビルダにて、from句に2つ以上のサブクエリを記載する方法を記載します。
SQL自体はなんてことないものですが、knex.jsでの書き方がわからず検索するのに時間がかかったため備忘録も込めて。

■実行環境

// node
node -v
// v10.16.3

// Knex.js
npm list --depth=0 | grep knex
// knex@0.15.2

// SQL Server
SELECT @@VERSION
// Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64)  Nov 30 2018 12:57:58  Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)

■書き方

結論から記載すると、半直書きするしかありません。
ちなみにこの方法に従わなくても全て直書きする方法もあります。

const subQuery1 = knex.select('id').from('table1');
const subQuery2 = knex.select('name').from('table2');

knex
    .select('*')
    .from(
        knex.raw([
            knex.raw(subQuery1).wrap('(', ') as sub_1'),
            knex.raw(subQuery2).wrap('(', ') as sub_2'),
        ]),
    );

説明
knex.raw そのままのSQLを書くことが可能
wrap knex.rawの中で使用でき、クエリをラッピング(クエリの前後に何か挿入)できる


以上となります。
またknex.jsについて調べたことがあれば、都度更新していこうと思います。


,

2019年10月31日木曜日

Windows ServerでApacheを2.4.41へアップデートする。


オフィス狛 技術部のJoeです。

数か月前になりますが、Apache HTTP Web Server 2.4系における複数の脆弱性に対応した Apache HTTP Web Server 2.4.41 が公開されました。
https://www.apachelounge.com/

担当したプロジェクトのWindows Serverがアップデート要のバージョンであったため、Apacheのアップデートを行ったのですが、「インストーラーをダウンロードして実行」のように簡単にはいかなかったので、備忘も兼ねて記事にしてみました。

アップデートを実施した環境は以下になります。
※OSやバージョンにより手順が異なる場合があるのでご注意ください。
OS:Windows Server 2012 R2
Apache:2.4.29 → 2.4.41

Apacheのアップデート

①Apacheのダウンロード

下記サイトよりダウンロードします。
https://www.apachelounge.com/

[手順]
左のメニューから「Downloads」を選択
→ ビルドバージョン(VCxx、VSxx など)を選択
→ 対象のApacheバージョンを選択してダウンロード

※ビルドバージョンの「VCxx、VSxx」は、ビルドされたVisual Studioのバージョンになるので、ご利用の環境により選択してください。
・VC14:Visual Studio C++ 2015
・VC15:Visual Studio C++ 2017
・VS16:Visual Studio C++ 2019

②アップデート前Apacheバックアップ

アップデート前の「Apache24」フォルダを別の場所へバックアップしておきます。
※アップデート後に、ソースや設定ファイル等を移行します

③コマンドプロンプト起動(管理者)

コマンドプロンプトを管理者として実行します。

④Apacheアンインストール

コマンドプロンプトで下記のコマンドを入力し、Apacheを停止してアンインストールします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k shutdown
httpd -k uninstall

⑤Apacheフォルダ配置

上記①でダウンロードしたファイルを解凍した新しいバージョンの「Apache24」フォルダを、Apacheのインストール先フォルダに配置します。(旧バージョンのフォルダはバックアップしているので、私はこのタイミングで削除しました)

⑥インストール

コマンドプロンプトで下記のコマンドを入力し、Apacheをインストールします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k install

⑦設定ファイル反映

ソースや設定ファイルを、アップデート後の「Apache24」フォルダに移行します。

⑧Apacheスタート

コマンドプロンプトで下記のコマンドを入力し、Apacheをスタートします。
cd [Apacheのインストール先フォルダ]\bin
httpd -k start

⑨バージョン確認

コマンドプロンプトで下記のコマンドを入力し、Apacheをスタートします。
cd [Apacheのインストール先フォルダ]\bin
httpd -v
以下のように出力され、正しくアップデートされました。
C:\Program Files\Apache Software Foundation\Apache24\bin>httpd -v
Server version: Apache/2.4.41 (Win64)
Apache Lounge VC14 Server built:   Aug 12 2019 10:48:01

⑩サービスの設定を確認

Apacheの自動起動などを設定している場合は、念のためサービスで設定を確認しておきます。


以上でアップデートが完了になります。

,

2019年10月30日水曜日

iOSアプリのアップデートをSirenを使って実装する。


オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

iOSに限ったことではないですが、
クライアントアプリとバックエンド(API)のバージョンの整合性を取るの面倒ですよね・・・・
バックエンドの都合で、どうしてもiOSを強制的にアップデートさせたい事もあります。

弊社の場合、プロジェクトにもよりますが、独自実装を行う方法と、プラグインを使う方法を使い分けています。
独自実装を行うのは、業務の兼ね合いだったり、バックエンド(API)の都合だったり、様々です。

今回はプラグインを使うパターンを説明します。
使うプラグインは「Siren」というものです。

ArtSabintsev/Siren

他のサイトなどで、導入方法は説明あるのですが、実用的な説明をしているものは少ないので、そこにポイントを絞って説明します。

と言っても・・・・・

let siren = Siren.shared
siren.presentationManager = PresentationManager(forceLanguageLocalization: .japanese)
siren.rulesManager = RulesManager(
    majorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .force), // A.b.c.d
    minorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .option), // a.B.c.d
    patchUpdateRules: Rules(promptFrequency: .daily, forAlertType: .option), // a.b.C.d
    revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .skip) // a.b.c.D
)
siren.wail()

これで終わりなんですけどね。
上記は、「AppDelegate」に記載します。

まずは、下記1行で、日本語化しています。
siren.presentationManager = PresentationManager(forceLanguageLocalization: .japanese)

次にRulesManagerを使って、複数パターンのルールを設定しています。
これで、バージョンアップの内容によって、アップデートの促し方を変えることが可能です。

コメントの「A.b.c.d」は、バージョン表示を示していて、大文字の部分が、変更となる箇所を表しています。
以下で細かく説明していきます。

メジャーアップデート(A.b.c.d)の時

例えば、現在使っているアプリのバージョンが「1.0.1.1」で、
App Storeにあるアプリのバージョンが「2.0.0.0」になっている場合です。
majorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .force), // A.b.c.d

この場合は、すぐにでもアップデートさせたいので、
promptFrequency: .immediately
で、アプリ起動時にすぐさまチェックし、
forAlertType: .force
で、アップデートを強制させています。
この時のダイアログ表示は、下記となります。


マイナーアップデート(a.B.c.d)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.2.0.0」になっている場合です。
minorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .option), // a.B.c.d

この場合も、出来ればすぐにアップデートして欲しいので、
promptFrequency: .immediately
で、アプリ起動時にすぐさまチェックしますが、
forAlertType: .option
で、アップデートはあくまで任意としています。
(まあ、毎回ダイアログが出るので、大抵の人はアップデートしてくれますけどね)
この時のダイアログ表示は、下記となります。


パッチアップデート(a.b.C.d)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.1.1.0」になっている場合です。
patchUpdateRules: Rules(promptFrequency: .daily, forAlertType: .option), // a.b.C.d

この場合も、出来ればすぐにアップデートして欲しいものの、
promptFrequency: .daily
で、チェック自体を1日に1回のみ行なって、
forAlertType: .option
で、アップデートもあくまで任意としています。
(この場合でも、大抵の人は2回目か3回目のダイアログ(つまり2日、3日目)ぐらいには、アップデートしてくれます)
この時のダイアログ表示は、下記となります。(マイナーアップデートと同じです。)


リビジョンアップデート(a.b.c.D)の時

例えば、現在使っているアプリのバージョンが「1.1.0.0」で、
App Storeにあるアプリのバージョンが「1.1.0.1」になっている場合です。
revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .skip) // a.b.c.D

この場合は、重要なアップデートではない事(少しの機能改善など)が多いので、
promptFrequency: .weekly
で、チェック自体を1週間に1回のみ行なって、
forAlertType: .skip
で、アップデート自体をスキップする事を可能としています。
(スキップの選択肢を出してしまうと、大抵の人はスキップして、アップデートしてくれません(泣))
この時のダイアログ表示は、下記となります。


以上となります。今回は、あくまで例として挙げましたが、
プロジェクトによって、色々な設定で組み合わせを試してみてはいかがでしょうか。


,

2019年10月29日火曜日

AWSのCloudWatchで期間を指定して統計情報を取得する。


オフィス狛 技術部のHammarです。

AWSのCloudWatchはいろいろな統計やデータ解析にも非常に役立つので、AWS環境で開発されている方は頻繁に見る機会があると思いますが、基本的にはグラフを見ながら、この時間にCPU使用率が~とか、ネットワーク数が~と見たりすると思います。

ただ、グラフではなく、この期間のこのCPU使用率の「平均値」とか、ネットワーク入力数の「合計」とか見たい場合があり、それらの情報もCloudWatchで確認することができたので、備忘録として記載したいと思います。

ある期間のEC2のCPU使用率の平均値を知りたい場合

①CloudWatchメニューの「メトリクス」を選択

②「すべてのメトリクス」タブで対象のEC2インスタンスの「CPUUtilization」(CPU使用率)にチェックをいれる

③「グラフ化したメトリクス」タブの統計欄で「平均」を選択する

④「グラフのオプション」タブのウィジェットタイプで「数値」を選択し、値に「時間範囲の値」を選択する

⑤画面右上の表示範囲を設定する
 「カスタム」を選択して細かな期間で表示可能です。
例えば直近1ヶ月等の平均値を表示したい場合は「絶対値」タブで開始日時、終了日時を設定し、「適用」ボタンをクリックします。

以上の設定でEC2のCPU使用率の平均値が数値で表示されます。


同じような設定方法で、その他のメトリクスの期間内の合計や平均値の数値情報のみを取得することが可能ですので、グラフ以外の使い方として利用してみてはいかがでしょうか。

2019年10月15日火曜日

iOSアプリから Amazon ConnectとLambdaで電話をかける。


こんにちは、オフィス狛 モバイル開発担当 Aika-yuy です。
今回の投稿は、iOSアプリから AmazonConnectとLambdaで任意の番号に電話をかける方法をご紹介します。


前回までのLambdaからAmazon Connectで電話をかける方法です

①Amazon Connectで電話番号、お問い合わせフローの作成
②IAMでロールを作成
③Lambda関数の作成
④実行!!


今回の手順

①Amazon CognitoでiOSアプリからアクセス権限を作成
②CognitoのロールにLambdaのアクセス権限を追加
③iOSアプリにCognitoとLambda呼び出しの設定


①Amazon CognitoでiOSアプリからアクセス権限を作成

・TOP画面からCognitoを検索して選択



・Identity Poolを作成します



・新しいIDプールの作成



・任意の名前をつける
・認証なしのユーザーを許可にチェックして作成



・許可を押すと自動でロールが作成されます



・サンプルコードをコピーします(後ほどAppDelegateに貼り付け)



②CognitoのロールにLambdaのアクセス権限を追加

・IAMに移動します
・サイドメニューのロールを選択
・先ほど自動作成されたロールが一覧に表示されています
・Unauthの方をクリックして下さい



・インラインポリシーの追加を押して下さい



・サービスはLambdaを選択
・アクションはInvokeFunctionにチェックを入れて下さい
・ポリシーの確認ボタンを押します



・ARNの追加で以前に作った、LambdaFunctionのARNをペーストして下さい



・任意の名前をつけ、ポリシーの作成ボタンを押して下さい



・ポリシーが追加されていれば、完了です
・次はiOSアプリを作成していきます

③iOSアプリにCognitoとLambda呼び出しの設定

・今回は以下をpod installしました

pod 'AWSMobileClient', '~> 2.6.13'  # For AWSMobileClient
pod 'AWSCognito'
pod 'AWSLambda'

AppDelegateに先ほどのサンプルコードをそのまま貼り付けます

import AWSCognito//インポートを忘れずに

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.APNortheast1,identityPoolId:"identityPoolId")
        let configuration = AWSServiceConfiguration(region:.APNortheast1, credentialsProvider:credentialsProvider)
        AWSServiceManager.default().defaultServiceConfiguration = configuration
        return true
    }

viewControllerにボタンを押したらLambdaFunctionを叩くようにします

import AWSLambda//インポートを忘れずに

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    @IBAction func callButtanTapped(_ sender: Any) {
        let lambdaInvoker = AWSLambdaInvoker.default()
        
        lambdaInvoker.invokeFunction("testCall", jsonObject: nil).continueWith(block: {(task:AWSTask) -> Any? in
            if let error = task.error {
                print("Error: \(error)")
                return nil
            }
            return nil
        })
    }
}

ボタンを押すと電話がかけられるようになりました!!!

,