RMagickのためにImageMagick6をインストール

RMagickはImageMagick7系では動作しないので、ImageMagick6系をインストールする必要があります。少し前にこのトラブルに遭遇していたのですが、今でも同じ状況のようです。

Homebrewで以下のようにコマンドを実行して対応しました。

$ brew uninstall imagemagick
$ brew install imagemagick@6
$ brew link --force imagemagick@6

これでRMagickのインストールができるようになりました。

G SuiteのためにSPFレコードを設定

メール関連の設定というと、最近はG SuiteのためのDNSレコードを設定するくらいしかしていなかったのですが、メールに関する動向から離れすぎている気がしたので、SendGridの初心者向けセミナーに参加しました。

sendgrid.doorkeeper.jp

SendGridはHeroku経由でずいぶんと利用させていただいていますが、本体の管理画面はほとんど使ったことがありませんでした。セミナーではメール送信に関する最近の動向、APIによるメール送信、マーケティングメール、各種セキュリティについて紹介がありました。特にSPFやDKIMについては、私はあまり知らなかったので、とても参考になりました。

私が管理しているG Suiteから送信されたメールについてSPFとDKIMを確認したところ、以下のようになっていました。

SPF: NEUTRAL
DKIM: PASS

SPFの設定がうまくいっていなかったようなので、以下の情報の通りに設定しました。

support.google.com

数時間後、DNSの変更が反映されたら、SPFについてもPASSになっていました。

SPF: PASS
DKIM: PASS

AKSwiftSlideMenuでiOSアプリにドロワーメニューを追加するチュートリアル

シンプルにドロワーメニューを追加できるライブラリを探していたら、これがよさそうだったのでチュートリアルをやってみました。

以下のリンクからSwift4のファイルをダウンロードしてXcodeで開いておきます。

github.com

次に、新規にXcodeでプロジェクトファイルを作成します。

  • BaseViewController.swiftとMenuViewController.swiftをコピーします。
  • Main.storyboardからMenu View Controllerをコピーします。
  • View Controllerを選択してから、Editor > Embed in > Navigation Controllerを選択します。
  • ViewControllerの基底クラスをUIViewControllerからBaseViewContollerに変更します。

この時点でViewController.swiftは以下のようになっています。

import UIKit

class ViewController: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.addSlideMenuButton()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

これでメニューは動作するようになりましたが、メニューを選択しても動作しない状態なので、次に画面の切り替えなどを設定していきます。

  • Assets.xcassetsからCameraIcon、HomeIcon、PlayIconをコピーします。
  • Main.storyboardにView Controllerを追加して、StoryBoard IDとRestoration IDを指定します。
  • New FileでCocoa Touch Classを追加して、ファイル名はPlayVC.swiftにします。
  • さきほど追加したMain.storyboardにView ControllerとPlayVC.swiftを関連付けます。

ViewController.swiftは以下のようになります。

import UIKit

class ViewController: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.addSlideMenuButton()
        self.title = "Home"
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

PlayVC.swiftは以下のようになります。

import UIKit

class PlayVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.addSlideMenuButton()
        self.title = "Play"
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */
}

Main.storyboardでHome画面からPlay画面にSegue (Show)を追加します。

ナビゲーションバーの色を変更するためには、Main.storyboardのNavigation ControllerのBar Tintで色を指定します。

Swiftの非同期処理のコードから指定した処理をメインスレッドで実行する方法

SwiftでiOSアプリを開発していた際、非同期処理のコードの中で一部の処理をメインスレッドで実行することが求められました。以下にAppleの公式ドキュメントがありました。

私の場合、ネットワークの通信状態を制御するコードをメインスレッドで実行する必要があったので、以下のように記述しました。

DispatchQueue.main.async {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}

4711 ポーチュガル ヘアトニック

ずっと前に何かの景品で柑橘系のトニックをもらって気に入っていたのですが、Amazonで偶然に見つけたので購入しました。

4711 ポーチュガル ヘアトニック 150ml

4711 ポーチュガル ヘアトニック 150ml

以前にもらった時は赤色のラベルだったような気がします。

テキストを音声に変換するサービス (Amazon Polly, Azure Cognitive Services)

英語の発表で合成音声を利用するというのはおもしろいなと思いました。

英語で動画コンテンツを作成するために、以前なら英語に堪能な人に依頼しようとするところでしたが、今ならテキストから音声に変換した結果のクオリティもよいだろうと考え、利用できそうなサービスを調べてみました。

Amazon

aws.amazon.com

Amazon PollyはAPIが用意されていて、そちらが本命の使い方なのだろうけど、Webインターフェースも用意されていて、簡単に利用することができました。また、音声のパターンも数種類から選ぶことができました。

f:id:iotaworks:20170831132631p:plain

Microsoft Azure

Cognitive ServicesのBing Speech APIでテキストから音声出力ができました。こちらもAmazonと同様にWeb APIが用意されています。

azure.microsoft.com

Amazon PollyのWebインターフェースを利用するためには、AWSのアカウントでログインする必要がありましたが、こちらはログインの必要なく利用できました。AWSアカウントを持っていない人に作業を依頼するなど、メンバーの構成によってはAmazonよりAzureのほうが手軽かもしれません。

Google

Google Text to Speech (TTS) というサービスがあるようですが、API等の情報を見つけることができませんでした。

Google翻訳は、翻訳結果を読み上げてくれますが、そのファイルは標準的な方法ではダウンロードできないようです。Sound of Textというサイトが内部的にGoogleのサービスを利用しているようで、入力したテキストに対応する音声をダウンロードできました。

scrap.php.xdomain.jp

まとめ

今回の私のケースだと、APIではなくWebインターフェースでサービスを利用したかったので、Amazon PollyかAzure Cognitive ServicesのBing Speech APIのどちらかを利用することになりそうです。プログラミングすることを少し覚悟していたので、Webインターフェースが用意されているのはうれしかったです。

Azure Database for PostgreSQLでpg_trgmを利用した日本語全文検索

AzureでPostgreSQLが利用できるようになりました。

セットアップ

Azureの管理画面からPostgreSQLデータベースを作成します。作成する際に、以下の情報を指定します。

  • サーバー名
  • サブスクリプション
  • リソース グループ
  • サーバー管理者ログイン名
  • パスワード
  • パスワードの確認
  • 場所
  • バージョン
  • 価格レベル

作成したデータベースにアクセスするためには、接続元のIPアドレスを登録する必要があります。Azureの管理画面の設定 > 接続のセキュリティから「自分のIPを追加」を押してから「保存」を押して、IPアドレスを登録します。

データベースへの接続

psqlコマンドでPostgreSQLデータベースに接続します。その際、ユーザー名にはusername@hostnameのフォーマットで指定します。ユーザー名のフォーマットが間違っていると、以下のようなエラーが出力されます。

psql: FATAL:  Invalid Username specified. Please check the Username and retry connection. The Username should be in <username@hostname> format.

psqlコマンドでユーザー名、ホスト名を指定します。接続先のデータベースには、最初はpostgresを指定します。

$ psql -U username@hostname -h hostname.postgres.database.azure.com -d postgres

データベースの作成

接続後、新規にデータベースを作成します。

hostname=> create database newdb;

接続先データベースを切り替えます。

hostname=> \c newdb
psql (9.6.4, server 9.6.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-SHA384, bits: 256, compression: off)
You are now connected to database "newdb" as user "username@hostname".

全文検索のサポート状況

pg_bigm

pg_bigmを有効にするコマンドを実行すると、以下のようなエラーが出力されます。2017年8月17日現在ではpg_bigmはサポートされていないようです。

hostname=> create extension pg_bigm;
ERROR: extension "pg_bigm" is not supported by Azure Database for PostgreSQL
DETAIL: Installing the extension "pg_bigm" failed, because it is not on the list of extensions supported by Azure Database for PostgreSQL.
HINT: To see the full list of supported extensions run: SELECT * FROM pg_available_extensions;

pg_trgm

利用可能な機能を確認すると、pg_trgmは利用できるようです。

hostname=> select name, default_version from pg_available_extensions where name like '%pg_trgm%';
  name   | default_version 
---------+-----------------
 pg_trgm | 1.3
(1 row)

テーブルとインデックスの作成

テーブルを作成します。

hostname=> create table records (id serial, body text);

pg_trgmを有効にします。

hostname=> create extension pg_trgm;
CREATE EXTENSION

インデックスを作成します。

hostname=> create index records_body on records using gin (body gin_trgm_ops);
CREATE INDEX

サンプルデータの登録と検索

サンプルデータを登録します。今回はパフォーマンスを評価するわけではないので、1件だけ登録します。

hostname=> insert into records (body) values ('吾輩は猫である。名前はまだ無い。');

SQLで検索します。

hostname=> select * from records where body like '%吾輩は猫%';
 id |               body               
----+----------------------------------
  1 | 吾輩は猫である。名前はまだ無い。
(1 row)

正常に検索できました。インデックスが利用されているか確認します。

hostname=> explain select * from records where body like '%吾輩は猫%';
                                 QUERY PLAN                                 
----------------------------------------------------------------------------
 Bitmap Heap Scan on records  (cost=16.00..20.01 rows=1 width=36)
   Recheck Cond: (body ~~ '%吾輩は猫%'::text)
   ->  Bitmap Index Scan on records_body  (cost=0.00..16.00 rows=1 width=0)
         Index Cond: (body ~~ '%吾輩は猫%'::text)
(4 rows)

検索する文字数が2文字以下だとインデックスが利用されず、シーケンシャルスキャンになります。

hostname=> explain select * from records where body like '%猫%';
                        QUERY PLAN                        
----------------------------------------------------------
 Seq Scan on records  (cost=0.00..25.88 rows=51 width=36)
   Filter: (body ~~ '%猫%'::text)

pg_trgmのメカニズムについては、以下のスライドにまとまっていたので、あとで読んでみたいと思います。