20200721のiOSに関する記事は14件です。

UIViewControllerでpresentすると循環参照する?!

結論

UIViewControllerでpresentすると循環参照するんですね。
windowのrootViewControllerを差し替える時とかに注意が必要ですね。

サンプル

playgroundにて下記のコードで検証

import Foundation
import UIKit
import PlaygroundSupport

class VC: UIViewController {

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        print("init")
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        print("init2")
    }

    deinit {
        print("deinitialized")
    }

}

var p: VC? = VC()

PlaygroundPage.current.liveView = p

var p2: VC? = VC()

p?.present(p2!, animated: true, completion: nil)

_ = p?.presentedViewController

p = nil

_ = p2
_ = p2?.presentingViewController
init
init
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iPhoneの通信速度をXcodeから一時的に変更する方法

デバッグの際などにiPhoneの通信速度を変更したい場合、iPhoneの設定アプリにあるNetwork Link Conditionerを使用することがあると思います。
これはとても便利なのですが、使用中においてそれを表す表示がないこともあり、切り忘れることがよくありました。

そこで、Xcode11からはiPhoneのNetwork Link Conditionerを切り忘れることがないように、通信速度を変更できる方法が提供されているので紹介します。

iPhoneをMacに接続しXcodeを開き、Xcode > Window > Devices and Simulatorsを選択します。
開いたウィンドウ内にDEVICE CONDITIONSというセクションがあり、そこでConditionをNetwork Linkにすることによって、通信速度を変更することができます。
スクリーンショット 2020-07-21 17.49.48.png

ここで通信速度を変更すると、Mac上でシュミレートの開始と停止ができます。
通信速度のシミュレート中はiPhoneのステータスバーが一部グレーに変更されます。そのグレーの部分をタップすると選択中のProfileが表示され、停止することができます。またiPhoneとMacの接続を切っても同様に、通信速度のシミュレートは停止されます。
IMG_5240.PNG
IMG_5241.PNG

以上です。この方法を使うと通信速度の変更を戻し忘れることがなくなると思います。ぜひ試してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase iOS SDKをCarthageでアップデート(6.28.0以降)するとエラーになる問題の対応方法

はじめに

現在、Firebase iOS SDK(Crashlytics)をCarthageで管理しているのですが、6.28.0以降にアップデートしたところ、UnitTestや実行時にエラーが発生するようになり、ハマったので対応方法についてまとめました。

** TEST FAILED **
Testing failed:
Undefined symbol: OBJC_CLASS$_GDTCORUploadPackage
Testing cancelled because the build failed.

Undefined symbol: OBJC_CLASS$_GDTCORUploadPackage

エラーの詳細は以下となります。

ld: warning: Could not find or use auto-linked framework 'Firebase'
ld: warning: Could not find or use auto-linked framework 'FBLPromises'
Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_GDTCORUploadPackage", referenced from:
      objc-class-ref in GoogleDataTransportCCTSupport(GDTCCTPrioritizer.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

実行環境

環境 バージョン
macOS Catalina 10.15.6
Xcode 11.5.0
Firebase iOS SDK(Crashlytics) 6.28.0以降

原因

Firebase iOS Release Notesによると、Version 6.28.0 - July 14, 2020 の修正によって、GoogleDataTransportCCTSupport.frameworkが含まれなくなっており、これが原因でした。

Updated GoogleUtilities and GoogleDataTransport imports. The GoogleDataTransportCCTSupport dependency should no longer be linked along with Firebase (#5824).

DeepL翻訳

GoogleUtilities と GoogleDataTransport のインポートを更新しました。GoogleDataTransportCCTSupport依存関係がFirebaseと一緒にリンクされなくなりました (#5824)。

対応方法

Xcode projectから、GoogleDataTransportCCTSupport.framework を削除して、ビルド&再実行します。
これで、問題なく動作するようになります。

GoogleDataTransportCCTSupport.png

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSアプリ開発で参考にしたiOSエンジニア YouTuber

はじめに

本格的にiOSアプリを開発してから約2年が経ちましたが、始めた当初から独学しています。というのもインターネット上に情報が溢れているのでネイティブアプリの構成やデバッグ方法、作りたい機能は大体検索すれば出てきます。

参考にしたサービス:
・YouTube
・StackOverflow
・Medium
・Qiita
・Teratail
・Udemy

始めた当初は動画で見れるYouTubeは特に参考になりましたので、iOSエンジニアYouTuber(英語)を紹介します。

iOSエンジニアYouTuber

・ Let's build that app(私のメンター)
https://www.youtube.com/channel/UCuP2vJ6kRutQBfRmdcI92mA
アジア系アメリカ人のブライアン。この人がいなかったらiOSアプリ開発出来ていなかったと思います。
iOSプログラミングの基本、応用、デザインはほぼから彼から学びました。
このチャンネルは『このアプリを作ろう』的なシリーズ系をたくさん載せています。例えば、『Firebaseを使ってチャットアプリを作ってみよう』、『YouTubeを作ってみよう』、『Twitterを作ってみよう』などなど。
そのほかにもアルゴリズム系の動画を載せています。

・Code with Chris
https://www.youtube.com/user/CodeWithChris
ビギナー用の動画がたくさんあります。
1年前に公開された『How to make an app for Beginners』は243万回も再生されています。
ほかにもサードパーティーライブラリ(SDWEBIMAGEやSVProgressHUD)の使い方も解説しています。

・Sean Allen
https://www.youtube.com/channel/UCbTw29mcP12YlTt1EpUaVJw
基本的なSwift(ViewControllerの解説、ClassやStructの使い方、if文やswitch文の使い方、enumの使い方)の構造などを説明しています。

・ArchetApp
https://www.youtube.com/user/Archetapp
サードパーティーサービス(Firebase, Stripe, Realm)を使ったプロジェクト作成動画を載せています。

最後に

YouTubeは無料で見れる上、内容の濃いコンテンツが多いのでおすすめです。
技術的な面を解説している日本人iOSエンジニアYouTuberの方が見つからなかったため、外国人YouTuberを参考にしています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

YUV(YCbCr)のCVPixelBufferをBGRAに高速に変換する

ARKitのcapturedImageをmediapipeで利用する時など、CVPixelBufferのpixelFormatを変換する必要があることがあります。

以前の記事ではvImageを利用しましたが、vImageはCPUでの計算に最適化されており映像処理などには向かないものでした。
https://qiita.com/noppefoxwolf/items/b12d56e052664a21d8b6

そこで、GPUを利用してYCbCrをBGRAに変換するライブラリを作りました。Xcode12のSwiftPMで簡単に導入することができます。
https://github.com/noppefoxwolf/BlueDress

使い方

簡単に使うことができます。

import BlueDress

let converter = YCbCrImageBufferConverter()
let bgraBuffer = try converter.convertToBGRA(imageBuffer: imageBuffer)

変換の流れ

実際は変換ではなく、新規に新しいBGRAのPixelBufferを作ってそこにYCbCrを焼いています。

YCbCrのPixelBufferは、内部に2枚の画像を持っておりそれぞれ

  • Y: 輝度
  • CbCr 青と赤の色差

をサイズ違いでもっています。
YCbCrは以下の計算式で変換することが出来るので、Metalシェーダを使ってこの計算を行うというわけです。

R = 1.164(Y-16)                 + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)

実装で詰まりがちなところ

各PlaneのPixelFormat

YCbCrから2枚のPlaneを取り出す際に、最初はそれぞれをBGRAとして取り出していたのですがこれは間違いでそれぞれ

  • Y: r8Unorm
  • CbCr: rg8Unorm

で取り出す必要があります。
よく考えれば当然なのですが、ARKitのMetalテンプレートを読んで気がつきました。

CVPixelBufferとMTLTextureの関係

MTLTextureはあくまでCVPixelBufferの参照を持つ概念なので、MTLTextureに対して行った描画処理は特に取り出す必要はなく、MTLTextureのソースとして渡したCVPixelBufferに適用されます。

MTLTextureが-6660エラーで作れない

最終描画先の空のMTLTextureを作りたくて、そのソースとなる空のCVPixelBufferを自前で作っていたのですがどうもエラーでMTLTextureが作れませんでした。
MTLTextureを作る場合は、CVPixelBufferを生成する時にkCVPixelBufferMetalCompatibilityKey:trueを与えるのを忘れないようにしましょう。

Bundle.moduleが生成されない

これはXcode12が正式リリースされたら追記します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大学生が1週間でFlutterアプリを学んでリリースした過程(4日目)

こんにちはシオンです。

きてしまいましたか、これを書く時間が。。
今日、目が覚めて布団で寝返りを打った時からずっとこれを書く時間が来るのが憂鬱でした。
なぜデザインを終わらせなかったんだ、昨日の自分。
ツケはこのチャレンジが終わった時にしっかりと払ってもらうからな。(焼肉いきます。)

■目次

■今日こそデザインを決める
■いよいよプログラミング
■プログラミングを学ぶ
■まとめ

■今日こそデザインを決める

今日こそデザインを決めるというか、さっさと決めてコードを書き始めないと終わりません。
だってデザインが終わったらコードを書くための勉強をしてそれからコードを書かなきゃいけないんだから。

昨日の進捗はこれです。
1.png

全然進んでいませんね。
うんうん悩んでいてもあまり進まないことがわかったので、一旦3画面作ってみたいと思います。

ということで作ってみました。(すみません、記事書きながらやってるの忘れていました。。)
こちらです!
2.png

どうでしょうか?
なんとなく形にはなってますかね。画面増えてんじゃん!っていうツッコミはやめてください。

まあ赤点は回避できてるのではないかなと思います。
というか、何より時間がないのでこのまま行くしかないです。もしコード書きながら変更点出てきたら都度変えていきましょう。頑張れ明日の自分。

では、いよいよ目的だったFlutterに入っていきます!!
やっときました!(長かった。本当に。)
早速やっていきましょう!

■いよいよプログラミング

3.png

「Welcome to Android Studio」

ありがとうございます。やっっっときました。この画面に。どうぞ盛大に出迎えてください。

これが初期画面です。一度環境構築した時にここまではきていました。
4.png

では早速コードを書いていきましょう。
と言いたいところですが、コードを書くための知識が全くないため、何がなんだかわかりません。一から学んでいきましょう。

5.png

ということで参考書をキンドルで買いました。

これで一通り勉強していきたいと思います。

ただ、時間があまりにもなさすぎるので機能にしぼって学びます。
今回のアプリを作るために必要なコードとしては
・配置の仕方
・画面遷移
・テキスト入力
・画像表示
この辺を中心に学んでいきたいと思います。

■プログラミングを学ぶ

今更ながら、アプリ制作の工程に「プログラミングを学ぶ」という工程が組み込まれていることに驚きを隠せないですが。
マックでハンバーガー注文したら「ハンバーガーですね!わかりました、では作り方を学んできます!」と言われるみたいなことですかね。やばいですね。

とりあえず学んでいきます。
今回はFlutterの概要を学んで終わりにしたいと思います。
学びながらすごく簡単に言語化していくので、もしも間違っていたらすみません。その時は教えていただけると嬉しいです。

コードの書き方をみていきます。開発の背景とかはすっ飛ばします。

なるほど、どうやらFlutterでプログラミングをしていく上で大事になってくるのはwidget(ウィジェット)というものらしい。
widget自体はandroidスマホを使っている人は結構身近なものらしいですね。ホーム画面上に常に表示されるアプリのショートカット機能のことも、「ウィジェット」というらしいですね。

Flutterでのプログラミングでは、widgetとは「UI情報が入った箱」だと考えると良さそうです。この箱を自分の好みのデザインに積んでいくようなイメージ。
でこの箱には種類が2つあって、「中身の情報が変わらない箱」と「中身の情報が変化する箱」
前者をStatelessWidget、後者をStatefulWidgetという。この箱を画面の中で配置していくのが大体の流れになっているようですね。

だからこのwidgetの置き方・配置の仕方、2種類のWidgetの使い分けの仕方を学べばとりあえず画面を表示することはできそうです。
では、この参考書に従っておなじみのHelloWorldを書いて今日は終わりにしたいと思います。

このプログラミングの細かいところは飛ばしていきます。
ちなみに、やったこととしてはKaishoukunという一番大きな箱を用意。その中にStatelessWidgetをおく。そしてその真ん中にTextのUI情報を持った箱を用意。そのTextにHelloWorldと入力しました。

期限まで3日「HelloWorld」してから3日でアプリをリリースしたら割とすごいんじゃないか?と思ってワクワクしましたが、今のところ想像するに完成していない未来しか見えていないので、逆に絶望しています。
明日明後日の土日でなんとかしていきます。

6.png

明日もよろしくお願いいたします。

■まとめ

やっとプログラミングのスタートに立ちました。プログラミングの過程を公開するというテーマなのに、ここまで全くコードがなくてごめんなさい笑
ここからは学びながら、できるだけ早くコードを書いていきます。
リリースまで考えるとこの土日の2日間でコードを書き切る必要がありますが、果たしてどうなるのか。。

明日の自分がどこまで頑張るのか楽しみですね。頑張れ!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分でレイアウトを組んだ時の注意

UIをコードで組んだ時に

translatesAutoresizingMaskIntoConstraints = false

を入れてあげないと、自分が組んだUIレイアウトが優先されない。

自分の場合

レイアウトを組む際は、関数を定義してあげて使ってます。

func anchor(top: NSLayoutYAxisAnchor? = nil,
                left: NSLayoutXAxisAnchor? = nil,
                bottom: NSLayoutYAxisAnchor? = nil,
                right: NSLayoutXAxisAnchor? = nil,
                paddingTop: CGFloat = 0,
                paddingLeft: CGFloat = 0,
                paddingBottom: CGFloat = 0,
                paddingRight: CGFloat = 0,
                width: CGFloat? = nil,
                height: CGFloat? = nil) {
        translatesAutoresizingMaskIntoConstraints = false

こんな風に、書いてます。

これでanchor()を呼び出して使ってます。

使用例

let titleLabel : UILabel = {
    let label = UILabel()
    label.text = "hoge"
    label.font = UIFont.boldSystemFont(ofSize: 14)
    return label
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(titleLabel)
  titleLabel.anchor(top: view.safeAreaLayoutGuide.topAnchor,
            right: view.safeAreaLayoutGuide.rightAnchor,
            paddingTop: 50, paddingRight: 147, width: 100, height: 56)
  titleLabel.layer.cornerRadius = 56/2
}

みたいな感じで書いてあげればおそらくかけるのかなと思います。

image.png

こんな感じにできます。

レイアウトのコードは

https://github.com/tiking76/chatapp/blob/master/chatApp/Utiles/Extentions.swift

に置いてあります。

もっと詳しく知りたいよ!!ってひと

↓のスライドをみてね!

LTで登壇した時のスライド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swiftで自分でレイアウトを組んだ時の注意

UIをコードで組んだ時に

translatesAutoresizingMaskIntoConstraints = false

を入れてあげないと、自分が組んだUIレイアウトが優先されない。

自分の場合

レイアウトを組む際は、関数を定義してあげて使ってます。

func anchor(top: NSLayoutYAxisAnchor? = nil,
                left: NSLayoutXAxisAnchor? = nil,
                bottom: NSLayoutYAxisAnchor? = nil,
                right: NSLayoutXAxisAnchor? = nil,
                paddingTop: CGFloat = 0,
                paddingLeft: CGFloat = 0,
                paddingBottom: CGFloat = 0,
                paddingRight: CGFloat = 0,
                width: CGFloat? = nil,
                height: CGFloat? = nil) {
        translatesAutoresizingMaskIntoConstraints = false

こんな風に、書いてます。

これでanchor()を呼び出して使ってます。

使用例

let titleLabel : UILabel = {
    let label = UILabel()
    label.text = "hoge"
    label.font = UIFont.boldSystemFont(ofSize: 14)
    return label
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(titleLabel)
  titleLabel.anchor(top: view.safeAreaLayoutGuide.topAnchor,
            right: view.safeAreaLayoutGuide.rightAnchor,
            paddingTop: 50, paddingRight: 147, width: 100, height: 56)
  titleLabel.layer.cornerRadius = 56/2
}

みたいな感じで書いてあげればおそらくかけるのかなと思います。

image.png

こんな感じにできます。

レイアウトのコードは

https://github.com/tiking76/chatapp/blob/master/chatApp/Utiles/Extentions.swift

に置いてあります。

もっと詳しく知りたいよ!!ってひと

↓のスライドをみてね!

LTで登壇した時のスライド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIKitのレイアウトをコードで組んだ時の備忘録

UIをコードで組んだ時に

translatesAutoresizingMaskIntoConstraints = false

を入れてあげないと、自分が組んだUIレイアウトが優先されない。

自分の場合

レイアウトを組む際は、関数を定義してあげて使ってます。

func anchor(top: NSLayoutYAxisAnchor? = nil,
                left: NSLayoutXAxisAnchor? = nil,
                bottom: NSLayoutYAxisAnchor? = nil,
                right: NSLayoutXAxisAnchor? = nil,
                paddingTop: CGFloat = 0,
                paddingLeft: CGFloat = 0,
                paddingBottom: CGFloat = 0,
                paddingRight: CGFloat = 0,
                width: CGFloat? = nil,
                height: CGFloat? = nil) {
        translatesAutoresizingMaskIntoConstraints = false

こんな風に、書いてます。

これでanchor()を呼び出して使ってます。

使用例

let titleLabel : UILabel = {
    let label = UILabel()
    label.text = "hoge"
    label.font = UIFont.boldSystemFont(ofSize: 14)
    return label
}()

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(titleLabel)
  titleLabel.anchor(top: view.safeAreaLayoutGuide.topAnchor,
            right: view.safeAreaLayoutGuide.rightAnchor,
            paddingTop: 50, paddingRight: 147, width: 100, height: 56)
  titleLabel.layer.cornerRadius = 56/2
}

みたいな感じで書いてあげればおそらくかけるのかなと思います。

image.png

こんな感じにできます。

レイアウトのコードは

https://github.com/tiking76/chatapp/blob/master/chatApp/Utiles/Extentions.swift

に置いてあります。

もっと詳しく知りたいよ!!ってひと

↓のスライドをみてね!

LTで登壇した時のスライド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IKEv2のstrongSwanとiOSのVPNにChaCha20-Poly1305を使って高速化

iOS 13からIKEv2の暗号化アルゴリズムにChaCha20-Poly1305を使うことができるようになりました。特にRaspberry PiなどのAES専用命令を積んでいないARMプロセッサでVPNサーバーを構築している場合、ChaCha20-Poly1305を使うことで著しいスループットの向上が期待できます。
せっかくなので今回は暗号化にChaCha20-Poly1305、鍵導出にSHA-512、鍵交換にX25519を使うように設定してみます。

strongSwan側の設定

swanctlを使う場合

/etc/swanctl/swanctl.conf
connections {
    <コネクション名> {
        ...
        proposals = chacha20poly1305-prfsha512-curve25519, default
        ...
    }

    ...

    children {
        <子SA名> {
            ...
            esp_proposals = chacha20poly1305, default
            ...
        }
    }
}

旧来のipsec.confを使う場合

/etc/ipsec.conf
conn <コネクション名>
  ...
  ike=chacha20poly1305-prfsha512-curve25519
  esp=chacha20poly1305
  ...

iOS側の設定

XML(plist)でVPNプロファイルを記述します。

VPN.mobileconfig
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- プロファイル名 -->
    <key>PayloadDisplayName</key>
    <string>自宅用VPNプロファイル</string>
    <!-- プロファイルの識別子。適当なFQDNを逆向きにした形で書く -->
    <key>PayloadIdentifier</key>
    <string>com.example.profile.vpn</string>
    <!-- uuidgenなどで生成したユニークなUUID -->
    <key>PayloadUUID</key>
    <string>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
    <key>PayloadContent</key>
    <array>
        <dict>
            <!-- VPNプロファイルの識別子 -->
            <key>PayloadIdentifier</key>
            <string>com.example.profile.vpn.conf</string>
            <!-- これもuuidgenなどで適当に生成 -->
            <key>PayloadUUID</key>
            <string>yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</string>
            <key>PayloadType</key>
            <string>com.apple.vpn.managed</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- VPN名 -->
            <key>UserDefinedName</key>
            <string>自宅VPN</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- VPNサーバーのアドレス -->
                <key>RemoteAddress</key>
                <string>vpn.example.com</string>
                <!-- サーバーの証明書のCNまたはsubjectAltName -->
                <key>RemoteIdentifier</key>
                <string>vpn.example.com</string>
                <!-- クライアント証明書認証は使わないので空 -->
                <key>LocalIdentifier</key>
                <string></string>
                <!-- サーバーの証明書の発行者のCN。自己署名証明書を使う場合はRemoteIdentifierと同じになる -->
                <key>ServerCertificateIssuerCommonName</key>
                <string>vpn.example.com</string>
                <!-- これもサーバーの証明書のCNまたはsubjectAltName -->
                <key>ServerCertificateCommonName</key>
                <string>vpn.example.com</string>
                <!-- サーバーは証明書認証を使う -->
                <key>AuthenticationMethod</key>
                <string>Certificate</string>
                <!-- クライアントはEAP-MSCHAPv2で認証 -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <!-- EAPのユーザー名 -->
                <key>AuthName</key>
                <string>user_name</string>
                <!-- EAPのパスワード -->
                <key>AuthPassword</key>
                <string>my_password</string>
                <key>EnablePFS</key>
                <integer>1</integer>
                <!-- IKE SA の暗号化パラメータ -->
                <key>IKESecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <!-- DHグループ31 = X25519 -->
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
                <!-- Child SA の暗号化パラメータ -->
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
            </dict>
        </dict>
    </array>
</dict>
</plist>

このファイルを.mobileconfigという拡張子で保存し、メールかiCloud DriveでiOS端末に送ります。受信したファイルをiOS側で開くと設定画面からVPNプロファイルをインストールできるようになります。

速度測定

Raspberry Pi 4 (64ビット) のstrongSwanで立てたVPNサーバーで測定した結果です。
スピードテストサーバーはOPEN Project (via 20G SINET)を使用しました。
上から順にVPNなし、ChaCha20-Poly1305、AES_CBC_256/HMAC_SHA2_256_128です。ChaCha20-Poly1305を使うとVPNなしとほぼ変わらない速度が出ています。
IMG_1778.jpg

まとめ

AES専用命令のないサーバーでChaCha20-Poly1305を使うとパフォーマンスが圧倒的に向上することがわかりました。
VPNの速度が遅い場合、サーバーが対応している場合はChaCha20-Poly1305を使うようにしてみるといいかもしれません。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

strongSwanとiOSでIKEv2にChaCha20-Poly1305を使ってVPNを高速化

iOS 13からIKEv2の暗号化アルゴリズムにChaCha20-Poly1305を使うことができるようになりました。特にRaspberry PiなどのAES専用命令を積んでいないARMプロセッサでVPNサーバーを構築している場合、ChaCha20-Poly1305を使うことで著しいスループットの向上が期待できます。
せっかくなので今回は暗号化にChaCha20-Poly1305、鍵導出にSHA-512、鍵交換にX25519を使うように設定してみます。

strongSwan側の設定

swanctlを使う場合

/etc/swanctl/swanctl.conf
connections {
    <コネクション名> {
        ...
        proposals = chacha20poly1305-prfsha512-curve25519, default
        ...
    }

    ...

    children {
        <子SA名> {
            ...
            esp_proposals = chacha20poly1305, default
            ...
        }
    }
}

旧来のipsec.confを使う場合

/etc/ipsec.conf
conn <コネクション名>
  ...
  ike=chacha20poly1305-prfsha512-curve25519
  esp=chacha20poly1305
  ...

iOS側の設定

XML(plist)でVPNプロファイルを記述します。

VPN.mobileconfig
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- プロファイル名 -->
    <key>PayloadDisplayName</key>
    <string>自宅用VPNプロファイル</string>
    <!-- プロファイルの識別子。適当なFQDNを逆向きにした形で書く -->
    <key>PayloadIdentifier</key>
    <string>com.example.profile.vpn</string>
    <!-- uuidgenなどで生成したユニークなUUID -->
    <key>PayloadUUID</key>
    <string>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
    <key>PayloadContent</key>
    <array>
        <dict>
            <!-- VPNプロファイルの識別子 -->
            <key>PayloadIdentifier</key>
            <string>com.example.profile.vpn.conf</string>
            <!-- これもuuidgenなどで適当に生成 -->
            <key>PayloadUUID</key>
            <string>yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</string>
            <key>PayloadType</key>
            <string>com.apple.vpn.managed</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- VPN名 -->
            <key>UserDefinedName</key>
            <string>自宅VPN</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- VPNサーバーのアドレス -->
                <key>RemoteAddress</key>
                <string>vpn.example.com</string>
                <!-- サーバーの証明書のCNまたはsubjectAltName -->
                <key>RemoteIdentifier</key>
                <string>vpn.example.com</string>
                <!-- クライアント証明書認証は使わないので空 -->
                <key>LocalIdentifier</key>
                <string></string>
                <!-- サーバーの証明書の発行者のCN。自己署名証明書を使う場合はRemoteIdentifierと同じになる -->
                <key>ServerCertificateIssuerCommonName</key>
                <string>vpn.example.com</string>
                <!-- これもサーバーの証明書のCNまたはsubjectAltName -->
                <key>ServerCertificateCommonName</key>
                <string>vpn.example.com</string>
                <!-- サーバーは証明書認証を使う -->
                <key>AuthenticationMethod</key>
                <string>Certificate</string>
                <!-- クライアントはEAP-MSCHAPv2で認証 -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <!-- EAPのユーザー名 -->
                <key>AuthName</key>
                <string>user_name</string>
                <!-- EAPのパスワード -->
                <key>AuthPassword</key>
                <string>my_password</string>
                <key>EnablePFS</key>
                <integer>1</integer>
                <!-- IKE SA の暗号化パラメータ -->
                <key>IKESecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <!-- DHグループ31 = X25519 -->
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
                <!-- Child SA の暗号化パラメータ -->
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
            </dict>
        </dict>
    </array>
</dict>
</plist>

このファイルを.mobileconfigという拡張子で保存し、メールかiCloud DriveでiOS端末に送ります。受信したファイルをiOS側で開くと設定画面からVPNプロファイルをインストールできるようになります。

速度測定

Raspberry Pi 4 (64ビット) のstrongSwanで立てたVPNサーバーで測定した結果です。
スピードテストサーバーはOPEN Project (via 20G SINET)を使用しました。
上から順にVPNなし、ChaCha20-Poly1305、AES_CBC_256/HMAC_SHA2_256_128です。ChaCha20-Poly1305を使うとVPNなしとほぼ変わらない速度が出ています。
IMG_1778.jpg

まとめ

AES専用命令のないサーバーでChaCha20-Poly1305を使うとパフォーマンスが圧倒的に向上することがわかりました。
VPNの速度が遅い場合、サーバーが対応している場合はChaCha20-Poly1305を使うようにしてみるといいかもしれません。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IKEv2のstrongSwanとiOSのVPNにChaCha20-Poly1305を使う

iOS 13からIKEv2の暗号化アルゴリズムにChaCha20-Poly1305を使うことができるようになりました。特にRaspberry PiなどのAES専用命令を積んでいないARMプロセッサでVPNサーバーを構築している場合、ChaCha20-Poly1305を使うことで著しいスループットの向上が期待できます。
せっかくなので今回は暗号化にChaCha20-Poly1305、鍵導出にSHA-512、鍵交換にX25519を使うように設定してみます。

strongSwan側の設定

swanctlを使う場合

/etc/swanctl/swanctl.conf
connections {
    <コネクション名> {
        ...
        proposals = chacha20poly1305-prfsha512-curve25519, default
        ...
    }

    ...

    children {
        <子SA名> {
            ...
            esp_proposals = chacha20poly1305, default
            ...
        }
    }
}

旧来のipsec.confを使う場合

/etc/ipsec.conf
conn <コネクション名>
  ...
  ike=chacha20poly1305-prfsha512-curve25519
  esp=chacha20poly1305
  ...

iOS側の設定

XML(plist)でVPNプロファイルを記述します。

VPN.mobileconfig
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- プロファイル名 -->
    <key>PayloadDisplayName</key>
    <string>自宅用VPNプロファイル</string>
    <!-- プロファイルの識別子。適当なFQDNを逆向きにした形で書く -->
    <key>PayloadIdentifier</key>
    <string>com.example.profile.vpn</string>
    <!-- uuidgenなどで生成したユニークなUUID -->
    <key>PayloadUUID</key>
    <string>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
    <key>PayloadContent</key>
    <array>
        <dict>
            <!-- VPNプロファイルの識別子 -->
            <key>PayloadIdentifier</key>
            <string>com.example.profile.vpn.conf</string>
            <!-- これもuuidgenなどで適当に生成 -->
            <key>PayloadUUID</key>
            <string>yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</string>
            <key>PayloadType</key>
            <string>com.apple.vpn.managed</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- VPN名 -->
            <key>UserDefinedName</key>
            <string>自宅VPN</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- VPNサーバーのアドレス -->
                <key>RemoteAddress</key>
                <string>vpn.example.com</string>
                <!-- サーバーの証明書のCNまたはsubjectAltName -->
                <key>RemoteIdentifier</key>
                <string>vpn.example.com</string>
                <!-- クライアント証明書認証は使わないので空 -->
                <key>LocalIdentifier</key>
                <string></string>
                <!-- サーバーの証明書の発行者のCN。自己署名証明書を使う場合はRemoteIdentifierと同じになる -->
                <key>ServerCertificateIssuerCommonName</key>
                <string>vpn.example.com</string>
                <!-- これもサーバーの証明書のCNまたはsubjectAltName -->
                <key>ServerCertificateCommonName</key>
                <string>vpn.example.com</string>
                <!-- サーバーは証明書認証を使う -->
                <key>AuthenticationMethod</key>
                <string>Certificate</string>
                <!-- クライアントはEAP-MSCHAPv2で認証 -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <!-- EAPのユーザー名 -->
                <key>AuthName</key>
                <string>user_name</string>
                <!-- EAPのパスワード -->
                <key>AuthPassword</key>
                <string>my_password</string>
                <key>EnablePFS</key>
                <integer>1</integer>
                <!-- IKE SA の暗号化パラメータ -->
                <key>IKESecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <!-- DHグループ31 = X25519 -->
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
                <!-- Child SA の暗号化パラメータ -->
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>ChaCha20Poly1305</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-512</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>31</integer>
                </dict>
            </dict>
        </dict>
    </array>
</dict>
</plist>

このファイルを.mobileconfigという拡張子で保存し、メールかiCloud DriveでiOS端末に送ります。受信したファイルをiOS側で開くと設定画面からVPNプロファイルをインストールできるようになります。

速度測定

Raspberry Pi 4 (64ビット) のstrongSwanで立てたVPNサーバーで測定した結果です。
スピードテストサーバーはOPEN Project (via 20G SINET)を使用しました。
上から順にVPNなし、ChaCha20-Poly1305、AES_CBC_256/HMAC_SHA2_256_128です。ChaCha20-Poly1305を使うとVPNなしとほぼ変わらない速度が出ています。
IMG_1778.jpg

まとめ

AES専用命令のないサーバーでChaCha20-Poly1305を使うとパフォーマンスが圧倒的に向上することがわかりました。
VPNの速度が遅い場合、サーバーが対応している場合はChaCha20-Poly1305を使うようにしてみるといいかもしれません。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Udemyのswiftコース(英語のやつ)でiOSアプリ開発を学ぶ その2

勉強するコースはこれです:point_down:
https://www.udemy.com/course/ios-13-app-development-bootcamp/

進捗

セクション2まで終了(全部で36セクション)
Xcodeの使い方など開発環境についてのお話がメインだった

最初の感想

<Udemyのコースについて>
- 本当に手取り足取り教えてくれるので、知識ゼロからでも学べそうなレベル
- 英語のレベルは、アメリカの小学校高学年~中学校くらいのレベルな気がする(小難しい単語は出ない)
- 先生(Dr. Angela Yu)の英語は、少しイギリス英語アクセントっぽいが非常に聞き取りやすい(アジア人にとって分かりやすい英語だと思う)
- やや説明するスピードが速いが、英語字幕があるので大丈夫そう
- 先生の英語はとても勉強になるし、ちゃんとした授業形式になっているので学びにつながる
- これやっとけばセブ島留学とか行く必要ないんじゃないかとすら思う。。

<iOS開発について>
- レイヤーになっているところとかPhotoshopっぽい
- やはりwindowsとは違うので、操作には慣れが必要になる
- windowsよりも開発環境が全体的にデザインがポップな印象、ちょっとテンション上がる

今後の予定

特に問題ないので、この調子で進めていく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ARKitでバーチャル背景をつくる

例の機能をつくる

argif.gif
バーチャル背景は、ZoomやTikTokで見かける機能です。
それは、人物の背景を画像や動画に変えます。

ARKitを使うことで、かんたんにバーチャル背景をつくることができます。
(ARSCNViewを使います。)

ViewController.swift
@IBOutlet var sceneView: ARSCNView!
var virtualBackgroundNode = SCNNode()

人物だけを手前に残す(People Occlution)

背景をバーチャルにするために、人物だけを手前にのこす必要があります。
ARKitのPeople Occlutionという機能を使うことで、人物をARコンテンツの手前に表示できます。
これを設定しないと、ARコンテンツが人物をオーバーラップしてしまいます。

Occlutionなし
スクリーンショット 2020-07-21 0.16.29.png

Occlutionあり
スクリーンショット 2020-07-21 0.17.24.png

ARSessionにPeople Occlutionを設定します。

ViewController.swift
 let config = ARFaceTrackingConfiguration()                    
      if ARFaceTrackingConfiguration.supportsFrameSemantics(.personSegmentation) {
            config.frameSemantics.insert(.personSegmentation)
      } else {
            presentAlert(NSLocalizedString("この端末/OSではピープルオクルージョンを利用できません", comment: ""))
      }
 sceneView.session.run(config, options: [])

バーチャル背景を置く(SCNNode)

バーチャル背景をつくる

板マテリアルでSCNNodeをつくり、その表面に画像や動画をコンテンツとして貼ります。

貼り付けるコンテンツはSpriteKitのSKSceneとしてつくります。
動画を貼るためにこうしました、と思う。

画像の場合

ViewController.swift
//画像の縦横比が歪まないように調整しています
let width = uiImage.size.width
let height = uiImage.size.height
let mediaAspectRatio = Double(width / height)
let cgImage = uiImage.cgImage
let newUiImage = UIImage(cgImage: cgImage!, scale: 1.0, orientation: .up)

let skScene = SKScene(size: CGSize(width: 1000  * mediaAspectRatio, height: 1000))
let texture = SKTexture(image:newUiImage)
let skNode = SKSpriteNode(texture:texture)
skNode.position = CGPoint(x: skScene.size.width / 2.0, y: skScene.size.height / 2.0)
skNode.size = skScene.size
skNode.yScale = -1.0
skScene.addChild(skNode)

動画の場合

ViewController.swift
let avPlayer = AVPlayer(url: videoUrl)
//画像の縦横比が歪まないように調整しています
var mediaAspectRatio: Double!
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else { return (nil,nil) }
let size = track.naturalSize.applying(track.preferredTransform)
let resolution = (CGSize(width: abs(size.width), height:abs(size.height)),track.preferredTransform)
let width = resolution.0?.width
let height = resolution.0?.height
mediaAspectRatio = Double(width! / height! )
avPlayer.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none;
NotificationCenter.default.addObserver(self,
                                       selector: #selector(ViewController.didPlayToEnd),
                                       name: NSNotification.Name("AVPlayerItemDidPlayToEndTimeNotification"),
                                       object: avPlayer.currentItem)
let skScene = SKScene(size: CGSize(width: 1000 * mediaAspectRatio, height: 1000))
if resolution.1?.b != 0{
    skScene.size = CGSize(width: 1000, height: 1000 )
    skScene.zRotation = 1.5708
} else if resolution.1?.a != 1.0 {
    skScene.zRotation = 1.5708 * 2
}

let skNode = SKVideoNode(avPlayer: avPlayer)
skNode.position = CGPoint(x: skScene.size.width / 2.0, y: skScene.size.height / 2.0)
skNode.size = skScene.size
skNode.yScale = -1.0
if resolution.1?.b != 0{
     skNode.zRotation = 1.5708
} else if resolution.1?.a != 1.0 {
     skNode.zRotation = 1.5708 * 2
}
skNode.play()
skScene.addChild(skNode)

ARKit(ARSCNView)で扱えるSCNNodeにコンテンツを貼り付けます。

ViewController.swift
virtualBackgroundNode.geometry = SCNPlane(width: size, height: size)
let material = SCNMaterial()
material.diffuse.contents = skScene
virtualBackgroundNode.geometry?.materials = [material]
virtualBackgroundNode.scale = SCNVector3(1.7  * mediaAspectRatio, 1.7, 1)
sceneView.scene.rootNode.addChildNode(node)

バーチャル背景をカメラの前に置く

ViewController.swift
let cameraPosition = sceneView.pointOfView?.scale
let position = SCNVector3(cameraPosition!.x, cameraPosition!.y, cameraPosition!.z - 10)
virtualBackgroundNode.position = position

バーチャル背景の位置を固定する

このままだと、端末カメラを動かすと、バーチャル背景がその置かれた「場所」にステイしてしまいます。
スクリーンショット 2020-07-21 0.24.39.png

バーチャル背景がカメラを追いかけて、ずっとその前に居続けるようにさせます。
スクリーンショット 2020-07-21 0.29.53.png

ARSCNViewのrendererメソッドでバーチャル背景SCNNodeにカメラ前のポジションを伝え続けます。

ViewController.swift
var frontOfCamera = SCNVector3()
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
     let position = SCNVector3(x: 0, y: 0, z: -10.0) // ノードの位置は、左右:0m 上下:0m 奥に50cm
        if let camera = sceneView.pointOfView {
            virtualBackgroundNode.position = camera.convertPosition(position, to: nil)
            virtualBackgroundNode.eulerAngles = camera.eulerAngles
        }
    }

バーチャル背景の大きさを変えられるようにする

このままだと、背景のサイズが画面のフレームにあっているかわかりません。
なので、ユーザーがピンチ・ジェスチュアで背景の大きさをちょうど良いサイズに変えられるようにします。

ViewController.swift
var lastGestureScale:Float = 1

@objc func scenePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
     switch recognizer.state {
     case .began:
         let logation = recognizer.location(in: sceneView)
         let hitResults = sceneView.hitTest(logation, options: [SCNHitTestOption.ignoreHiddenNodes:true])
         if hitResults.count > 0 {
             guard let node = hitResults.first?.node else {return}
             materialNode = node
         }
         lastGestureScale = 1
     case .changed:
         let newGestureScale: Float = Float(recognizer.scale)

         let diff = newGestureScale - lastGestureScale

         let currentScale = virtualBackgroundNode.scale

         virtualBackgroundNode.scale = SCNVector3Make(
             currentScale.x * (1 + diff),
             currentScale.y * (1 + diff),
             currentScale.z * (1 + diff)
         )
         lastGestureScale = newGestureScale
     default :break
     }
}
ViewController.swift
let pinch = UIPinchGestureRecognizer(
            target: self,
            action: #selector(type(of: self).scenePinchGesture(_:))
        )

pinch.delegate = self
sceneView.addGestureRecognizer(pinch)

ピンチ・ジェスチュアでSCNNodeを操作する方法は、天才プログラマーK-Boyさんのコードをお借りしました。ありがとうございます。

実際のアプリはこちら

ARCamera.
AppStore:https://apps.apple.com/us/app/id1488699740

GitHub:https://github.com/john-rocky/ARCamera

ぼくのTwitterをフォローしてください。お願いします。
https://twitter.com/JackdeS11

お仕事のご依頼をこのメールにお願いします。
rockyshikoku@gmail.com

あと、Looks Good For Me(わるくないね)、押してください。
        ここです ↓
チャオ?!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む