20200704のiOSに関する記事は7件です。

Kobiton でスマートフォンのリモートテストを試してみた

概要

昨今のテレワーク推進の中、スマートフォン開発における課題となるのが実機による動作検証です。
多種多様なスマートフォン端末の準備・利用は、利用者それぞれがテレワークしていると難しくなります。

今回は上記課題を解決しうるリモート実機動作検証サービス Kobiton について調査しました。

TL; DR

  • Kobiton https://kobiton.com/ はリモート実機検証できるサービス
  • ブラウザからリモートの実機を操作して手動・自動での動作検証が可能
  • デスクトップアプリを利用すると、自分たちで用意した実機を使っての動作検証も可能
  • 30日間無料トライアル

リモートの実機による動作検証

Kobiton がクラウド上に用意した実機を使って動作確認を行うことができます。

利用可能な端末

利用可能な実機の一覧は以下のURLから参照できます。

https://docs.kobiton.com/devices-list/

2020年7月4日時点で208種類のAndroid・iOS端末を利用できます。

端末一覧

ログイン後には以下のような画面から利用する端末を選ぶことができます。

ログイン後端末一覧

動作検証セッション

セッション中

動作検証中のセッションは以下のような画面です(以下の画面はAndroidの場合です)。
スワイプ等の操作は少々ラグがありますが、ブラウザから操作できていることを考えると十分でしょう。

セッション

右上の「Install Apps」ボタンから、自分で登録したアプリをインストールすることができます。
これで自分で開発したアプリの動作確認ができます。

セッションログ

セッション終了後はログが残ります。

セッションログ一覧

動作確認した動画も自動で残ります。

セッションログ overview

セッションの詳細では、どの時点でどんな操作をしたか閲覧することもできます。

セッションログ details

自分たちで用意した実機による動作検証

Kobiton がクラウド上に準備した端末は種類も多く便利ですが、不便な点もあります。
他の人が使用中だと使えなかったり、パブリックなネットワークにしかアクセスできなかったりします。

これらの問題点を解決するため、Kobiton のデスクトップアプリを介して自分たちで用意した実機を使用することができます。
上記の問題点を解決しつつ、端末の管理は Kobiton のクラウド上の端末と同様に行うことができます。

デスクトップアプリの準備

詳細は公式のドキュメントを見ていただくとよいかと思います。

Kobiton のデスクトップアプリは Mac OS でのみ使うことができます。
Windows アプリはないようです。
iOS 端末にも対応するためでしょうか。

その後、 Android と iOS の設定を行います。
Android であれば JDK および Android SDK のパスを指定します。
iOS であれば Xcode のパスおよび証明書・プロビジョニングプロファイルの設定を行います。

端末の登録

端末を接続すると、 Kobiton のアプリケーションが端末にインストールされます。
基本的にはこれだけで問題ないです。

私が設定したときは、 追加で端末のデバイスロックを解除しておく必要がありました。
AndroidおよびiOSのトラブルシューティングのドキュメントが整備されているので、設定がうまくいかないときはご確認ください。

端末の利用

設定が終われば、あとは Kobiton が提供する端末と同じように自分のローカル端末で動作確認を行うことができます。

ローカルデバイス

まとめ

今回は Kobiton による実機端末を利用した動作確認について簡単に紹介しました。
Kobiton が提供する端末に加えて、自分の端末を利用することができます。

Kobiton ではさらにテストの自動実行や各種便利APIの利用ができるようです。

Free Trial もあるため、興味が湧きましたらぜひ試して記事を公開してください :pray:

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

[RxSwift]flatMap系とcombineLatestの違い

flatMap

使い方

a.flatMap(b),a.flatMapFirst(b), a.flatMapLatest(b)

特徴

aonNextの処理に、bonNextの全処理を掛け合わせていく。

aへのある要素への掛け合わせる処理が終わらないうちに、aへさらに次の要素が流れてきた場合、
flatMapFirstは次の要素を無視する
flatMapLatestは現在の要素をキャンセルし次の要素の処理を行う
flatMapは次の要素も現在の要素も両方行う。

combineLatest

使い方

Observable.combineLatest(a, b) { $0 + $1 }

特徴

abの最新の要素を掛け合わせる。

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

iOS / Web版Twitterの指定したページへのリンクをホーム画面に追加する

TL;DR

Web版Twitterをホーム画面に追加する
-> PWAになり追加時のURLが反映されない

別のページを経由して開けば解決
-> dataスキーム

data URI scheme

  • dataスキーム
  • 外部リソースと同じようにデータを読み込む方法
  • アドレスバーに入力する

iOSの「ホーム画面に追加」は後からURLを書き換えられない
-> 追加時用のTwitterを開かないパターンが必要
-> 端末がネットに繋がっているかで条件分岐(他にも方法はあると思う)

data:text/html,
<script>
if (navigator.onLine)
  location.href='開くURL'
</script>

実際の手順

  1. 上記のdataスキームの「開くURL」部分を変更
  2. オフラインの状態でブラウザのアドレスバーに入力
  3. ホーム画面に追加

問題点

ホーム画面に表示されるアイコンが真っ白なので複数追加すると分かりにくい

↓↓↓

ページに背景色を設定する

<style>
  html {background: 色}
<style>

アイコンの画像を設定する

<link rel="apple-touch-icon-precomposed" href="画像のURL">

以上

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

【Swift】ScrollViewがカクつく時の対処法

下記ブログの転載です!
https://rc-code.info/ios/post-273/

iOSにて、UIScrollView のカクつきを改善する必要があったので備忘録。
今回は UIScrollViewDelegate.scrollViewDidScroll を利用している時の対処法を紹介します。
 

UIScrollViewがカクつく理由

まず UIScrollView に限らず描画がカクつく場合、その理由は mainthread を圧迫していることがほとんどです。

mainthread については、こちらを参考にしていただければと思います。

scrollViewDidScroll内の処理を見直す

さて、今回解決したいのは UIScrollView のカクつきですが、scrollViewDidScroll という UIScrollView.Delegate の処理が重たくなりすぎているというケースがよく見られます。

後述する対策は scrollViewDidScroll だけでなく、他の UIScrollView.Delegate の関数にも有効な場合があるので、参考にしてみてください。

UIScrollView のカクつきを再現する負荷試験

下記は extensionscrollViewDidScroll を定義したサンプルになります。


extension ViewController: UIScrollViewDelegate {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        var count: Int = 0
        var array: [Int] = []
        repeat {
            array.append(count)
            array.reverse()
            count += 1
        } while(count < 200)
    }
}

上記のソースでは、スクロールされる度に 0〜199 までの数字を配列に格納してはリバースするという再帰処理を行なっています。

count を array に格納しリバース、countに+1 (array=[0]、count=1 になる)
count を array に格納しリバース、countに+1 (array=[1,0]、count=2 になる)
  以下、199まで繰り返し、、、

この処理が少しでもスクロールされる度に行われるので、なかなか重いタスクとなります。
scrollViewDidScroll という関数は、スクロール というUI操作に紐づいているので、この重たい処理は mainthread にて処理されてしまいます。
つまり、scrollViewDidScroll 関数内に負荷がかかると画面更新に影響するということです。
実際にこの処理を加えてUIScrollViewをスクロールしていただくと、カクつくことが分かると思います。

カクつきを解消する方法

class ViewController: UIViewController {

        //スクロール処理用Queue
    private let scrollSessionQueue = DispatchQueue(label: "ScrollSessionQueue")

        ...以下省略
}

extension ViewController: UIScrollViewDelegate {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        // mainthread を圧迫しないように、別スレッドで処理
        scrollSessionQueue.async {

            var count: Int = 0
            var array: [Int] = []
            repeat {
                array.append(count)
                array.reverse()
                count += 1
            } while(count < 200)
        }
    }
}

上記のソースでは、ViewControllerscrollLogSessionQueue というQueueを新たに呼び出しています。

そして scrollViewDidScroll 内の処理を scrollSessionQueue.async で囲むことで、処理を mainthread から別threadに移譲しています。
すなわち、処理は mainthread ではなく、UIに関連しない別の thread にて処理されることになります。

実際に動かしてみると、スクロールが滑らかになっていることが分かると思います。
DispatchQueue の詳しい生成方法については こちら を参考にしていただければと思います。

修正後の注意点

ただ、別threadにて処理を行う際には注意が必要です。
主に挙げられる注意点は

①別threadにすれば100%画面更新に影響がなくなるわけではない
②別threadに移譲するので、実行タイミングがズレる

という2点です。

①についてですが、 scrollLogSessionQueue.async で別スレッドを使ってもCPUやGPUが圧迫されることは免れませんし、別スレッド内の処理によっては再度 mainthread への移譲が行われるという事もあり得ます。
なので100%影響がなくなるというわけではありません。
サンプルコード scrollLogSessionQueue.async の中で行われている処理の負荷を上げていくと、この事が分かるかと思います。

②について、指定したthreadに処理を移譲するので、当然ながら処理は画面と同期しません。
画面的にはスムーズにスクロールしたり、スクロールが既に完了していても、scrollLogSessionQueue.async 内の処理が同様に完了しているとは限らないので、注意が必要です。
 

検証Playground

検証Playground Git
 

検証環境

Mac: 10.14.4
XCode: 10.2
Swift: 5.0
 

参考ドキュメント

公式 DispatchQueue ドキュメント
公式 UIScrollView ドキュメント
 
 

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

【Swift】WKWebViewでJavaScriptのコールバックを受けつける(WKUserContentControllerの使い方)

下記ブログの転載です!
https://rc-code.info/ios/post-194/

iOSにて、WKWebView でJavaScriptのコールバックを受けつける必要があったので備忘録。
今回は WKUserContentController を利用した方法を紹介します。
 

WKWebView の使い方

まずは WKWebView の基本的な使い方ですが、こちらを参考にしていただければと思います。
 

WKUserContentController の使い方

WKUserContentControllerJavaScriptからのコールバック受信や、スクリプトをWebビューに挿入する方法を提供します。
下記が導入のサンプルコードです。


import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func loadView() {

        // ① WKUserContentController の生成
        let userContentController = WKUserContentController()
        // ② WKUserContentController にコールバックハンドラを登録
        userContentController.add(self, name: "jsCallbackHandler")

        let webConfiguration = WKWebViewConfiguration()
        // ③ WKWebViewConfiguration に生成した WKUserContentController を登録する
        webConfiguration.userContentController = userContentController

        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        view = webView
    }
}

// ④ WKScriptMessageHandler のデリゲートを記載
extension ViewController: WKScriptMessageHandler {

    // ⑤ JavaScript から呼び出されるコールバックハンドラ関数を記載
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
            case "jsCallbackHandler":
                print("Javascript message arrived: jsCallbackHandler")
            default:
                return
        }
    }
}

1 WKUserContentController の生成

WKUserContentControllerWKWebViewConfigurationに格納する必要があるので、まずはインスタンスを生成します。
 

2 WKUserContentController にコールバックハンドラを登録

WKUserContentControlleradd関数を用いて、JavaScriptから実行したい関数を持つクラスインスタンス(WKScriptMessageHandlerプロトコルに準拠したクラス)を第一引数に設定し、コールバックの判別に利用するためのStringname引数に設定します。
このadd関数は何度も実行できるので、複数のコールバックを処理したい場合はname引数を変更して実行すれば、後述するコールバック関数内で判定することができます。
 

3 WKWebViewConfiguration に生成した WKUserContentController を登録する

生成したWKUserContentControllerインスタンスは、WKWebViewConfigurationuserContentControllerプロパティに格納します。
 

4 WKScriptMessageHandler のデリゲートを記載

上記ViewControllerはデリゲート先としてWKUserContentControllerに渡され登録されているので、WKScriptMessageHandlerプロトコルに準拠させる必要があります。
 

5 JavaScript から呼び出されるコールバックハンドラ関数を記載

WKScriptMessageHandlerプロトコルに準拠させるために、userContentController関数を実装します。この関数を実装する事で、Javascriptからのコールバックを受け付けることができるようになります。

JSからは下記のように、②の箇所のadd関数にてname引数に指定したコールバックを実行します。

    webkit.messageHandlers.jsCallbackHandler.postMessage("Message from Javascript");

コールバックハンドラ内ではmessage.nameプロパティでコールバック名を取得することができるので、サンプルコードのようにswitch文等で判定する事で、処理を分岐させる事ができます。
 

検証Playground

検証Playground Git
 

検証環境

Mac: 10.13.6
XCode: 10.1
Swift: 4.2.1
 

参考ドキュメント

公式 WKWebView ドキュメント
公式 WKWebViewConfiguration ドキュメント
公式 WKUserContentController ドキュメント
 
 

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

【Swift】WKWebViewの基本的な使い方

下記ブログの転載です!
https://rc-code.info/ios/post-241/

iOSにて、Webページを表示する必要があったので、WKWebView の基本的な使い方を備忘録。
 

WKWebView の使い方

WKWebView の基本的な使い方です。
下記が公式のサンプルコードです。

WKWebView を利用する際は UIKitWebKit を import します。
WKWebView の initialize は CGRectWKWebViewConfiguration を引数にとります。
該当するURLページを開くには、WKWebView の load関数 を実行します。

import UIKit
// 1 WebKit の import
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func loadView() {

        // 2 WKWebViewConfiguration の生成
        let webConfiguration = WKWebViewConfiguration()
        // 3 WKWebView に Configuration を引き渡し initialize
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        // 4 WKUIDelegate の移譲先として self を登録
        webView.uiDelegate = self
        // 5 WKNavigationDelegate の移譲先として self を登録
        webView.navigationDelegate = self
        // 6 view に webView を割り当て
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // 7 URLオブジェクトを生成
        let myURL = URL(string:"https://www.apple.com")
        // 8 URLRequestオブジェクトを生成
        let myRequest = URLRequest(url: myURL!)

        // 9 URLを WebView にロード
        webView.load(myRequest)
    }
}

// MARK: - 10 WKWebView ui delegate
extension ViewController: WKUIDelegate {
    // delegate
}

// MARK: - 11 WKWebView WKNavigation delegate
extension ViewController: WKNavigationDelegate {
    // delegate
}

 

1 WebKit の import

WebView関連のコードを扱うにはWebKitimportする必要があります。
冒頭にimport WebKitを記載すると、XcodeにてWebView関連の補完が効くようになるかと思います。

2 WKWebViewConfiguration の生成

WKWebViewConfigurationWKWebViewの初期化時に参照される設定プロパティが含まれるクラスです。
WKWebViewConfigurationを使用すると、Webページがレンダリングされるまでの時間、メディア再生の処理方法、ユーザーが選択できるアイテムの細分性、およびその他の多くのオプションを決定できます。

WKWebViewConfigurationは、WKWebViewが最初に初期化されたときにのみ使用されます。このクラスを使用して、WKWebViewの作成後にその設定を変更することはできません

3 WKWebView を initialize

WKWebViewの初期化にはframe引数WebViewのサイズconfiguration引数に生成したWKWebViewConfigurationのインスタンスを引き渡します。

4 WKUIDelegate の移譲先として self を登録

WKWebViewuiDelegateプロパティにselfをセットする事で、インターフェースに関わるWebViewの関数をクラス内で扱う事ができるようになります。

5 WKNavigationDelegate の移譲先として self を登録

WKWebViewnavigationDelegateプロパティにselfをセットする事で、振る舞いに関わるWebViewの関数をクラス内で扱う事ができるようになります。

6 view に webView を割り当て

生成したWKWebViewのインスタンスをControllerのviewに割り当てます。

7 URLオブジェクトを生成

表示したいURLのStringをURLクラスのstring引数に指定し、インスタンスを生成します。

8 URLRequestオブジェクトを生成

生成したURLクラスのインスタンスをURLRequestクラスのurl引数に指定し、インスタンス生成します。

9 URLを WebView にロード

WKWebViewload関数にURLRequestインスタンスを引き渡し、実行します。
この関数を叩くことで、webViewのリクエストが走ります。

10 WKWebView ui delegate

WKWebView ui delegate の部分には、WKWebViewインターフェースに関わるdelegate関数を記述します。
詳細は公式ドキュメント参照。
 

11 WKWebView WKNavigation delegate

WKWebView WKNavigation delegate の部分には、WebView振る舞いに関わるdelegate関数を記述します。
詳細は公式ドキュメント参照。
 

実行環境

Mac: 10.13.6
XCode: 10.1
Swift: 4.2.1
 

参考ドキュメント

公式 WKWebView ドキュメント
公式 WKWebView ui delegate ドキュメント
公式 WKWebView WKNavigation delegate ドキュメント
 
 

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

【Swift】WKPreferences の minimumFontSize が効かない

下記ブログの転載です!
https://rc-code.info/ios/post-236/

iOSのWKWebViewConfigrationに設定するWKPreferencesminimumFontSizeが効かないケースがあったので調査&備忘録。
 

WKWebView の使い方

WKWebViewの基本的な使い方に関しては こちら にまとめております。
合わせてご覧ください。
 

WKPreferences minimumFontSize の設定方法

WKPreferencesはWebビューの基本設定をカプセル化するオブジェクトです。(公式ドキュメント
このうちminimumFontSizeptサイズでフォントの最小サイズを決めるプロパティです。
まずは使い方を記載します。
 


import UIKit
// ① WebKit の import
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!

    override func loadView() {

        // ② WKWebViewConfiguration の生成
        let webConfiguration = WKWebViewConfiguration()
        // ③ WKPreferences の生成
        let preferences = WKPreferences()
        // ④ WKPreferences の minimumFontSize プロパティに値を指定
        preferences.minimumFontSize = 100.0
        // ⑤ WKWebViewConfiguration に WKPreferences のインスタンスを設定
        webConfiguration.preferences = preferences
        // ⑥ WKWebView に Configuration を引き渡し initialize
        webView = WKWebView(frame: CGRect(x: 150, y: 200, width: 200, height: 20), configuration: webConfiguration)
        // ⑦ view に WKWebView を割り当て
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        loadView()
        webView.load(URLRequest(url: URL(string: "https://www.apple.com/")!))
    }
}

 

上記のように、まずWKPreferencesのインスタンスを生成し、このプロパティであるminimumFontSize最小サイズにしたいフォントサイズをpt換算で指定します。
 

そしてWKPreferencesインスタンスWKWebViewConfigurationにセットし、このWKWebViewConfigurationWKWebViewの設定として使います。
 

minimumFontSize が効かないケース

さて本題ですが、このWebViewで様々なサイトを見てみると、minimumFontSizeを設定しているにも関わらず、指定フォントサイズ以下なってしまう場合が見受けられます。

検証した結果、この事象にはCSSのtext-size-adjustというスタイルが影響していることが分かりました。
 

text-size-adjust スタイルとは?

text-size-adjust (ベンダープレフィックスだと-webkit-text-size-adjust) は、一部のデバイスで使われるテキストの自動拡大アルゴリズムを制御するスタイルです。
対応していないブラウザーはこのプロパティを無視します。

一部のモバイル端末には、縦向き (Portrate) と横向き (Landscape) が切り替わった際に文字サイズを自動調整する機能があり、text-size-adjustはこの自動調整を制御します。
 

text-size-adjust が設定されると minimumFontSize が効かなくなる

検証を行なった結果、text-size-adjustによって縮小されたサイズはminimumFontSizeによって制限されたフォントサイズよりも小さくなることが分かりました。

念の為、その他のスタイル指定でフォントサイズの制限が無視されないか検証しています。
 

検証内容

最下部 Playground の Resources ディレクトリに様々なスタイル定義によるフォントサイズ変更を含んだHTMLファイルを用意しました。
minimumFontSizeを設定した WKWebViewにて、このHTMLをそれぞれ表示した結果、text-size-adjustを設定したHTMLのみ、minimumFontSizeで指定した値よりフォントサイズが小さくなりました。
 

下記画像が、minimumFontSize100を設定し、上部テキストに様々なスタイル定義で40px相当のフォントサイズを指定した画像です。
下記テキストは比較用のプレーンなテキストです。

通常スタイル適用画像

見た通り、スタイルで指定された40pxを無視し、minimumFontSizeに指定されている100pt相当の大きさで表示されています。
 

変わって次の画像がtext-size-adjustにを指定した時の画像です。

text-size-adjust適用画像

こちらの上部テキストでは、スタイルで指定された40pxが無視され、minimumFontSizeに指定されている100ptが適用されるはずですが、text-size-adjustに指定している10%が適用され、4px (40pxの10%)相当の大きさになっています。
 

まとめ

全ての要素・スタイルを検証できているわけではないので確実ではないですが、WKPreferencesminimumFontSizeを指定しているのにフォントが想定以下のサイズになってしまうようであれば、text-size-adjustの設定を疑ってみるのが良いかもしれません。
 
 

検証Playground

検証Playground Git
 

検証環境

Mac: 10.13.6
XCode: 10.1
Swift: 4.2.1
 

参考ドキュメント

WebKit
WKWebViewConfiguration
WKPreferences
text-size-adjust
 
 

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