20200911のiOSに関する記事は9件です。

RxSwift における subscribeOn と observeOn の違い

RxSwift などで処理をつなげて書いていると、ここはメインスレッドで、ここはバックグランドスレッドでなどと、処理によってスレッドを切り替えたい場合があるかと思います。そんな時は、Observable のプロパティとして提供されている subscribeOn または、observeOn を使用することでスレッドを切り替えて処理を実行することができます。今回はそんな subscribeOnobserveOn の違いについて簡単にまとめていこうかと思います。

その前に Scheduler

RxSwift では Observable や subscribe の処理をどのスレッドでどのよう(直列・並列)に処理をさばくかを決定する役割を Scheduler と呼びます。これについては後で触れますが、RxSwift を使用し、マルチスレッドで処理を行う際にはとても重要な知識です。

subscribeOn

subscribeOn は参照するストリームの元となる Observable の実行スレッドとそれに続くメソッドのスレッドを決定します。SerialDispatchQueueScheduler で Scheduler を生成し、バックグランドスレッドの直列処理で実行したサンプルが下記のようになります(呼び出しはメインスレッド)。どのように出力されるか結果を見る前に予想してみてください?‍?

    let ob = Observable<Int>.create { observer -> Disposable in
        for i in 0...10000 {
            if i == 10000 {
                print("observer called!")
                observer.onNext(1)
                observer.onCompleted()
            }
        }
        return Disposables.create()
    }
    let scheduler = SerialDispatchQueueScheduler(qos: .background)
    ob.subscribeOn(scheduler)
        .subscribe(onNext: { _ in
            print(1)
            for i in 0...100 {
                if i == 100 {
                    print(2)
                }
            }
        })
        .disposed(by: disposeBag)
    ob.subscribeOn(scheduler)
        .subscribe(onNext: { _ in
            print(3)
        })
        .disposed(by: disposeBag)

出力

// 全てバックグランドスレッド
observer called!
1
2
observer called!
3

上から順番にバックグランドスレッドにタスクが追加されていき、直列でそれぞれの処理が実行されているのが分かります。また、基本的には、同じスレッドで実行したい場合には Scheduler のインスタンスは統一する必要があるかと思います。(試しに同じ Schedler を別々のインスタンスで実行したところ直列で実行がなされませんでした?原因がわかる人教えください?‍♂️)

observeOn

observeOn はどの Scheduler で次の Observer にイベントを流すのかを決定します。MainScheduler.instanceSchedler を生成して、購読部分のみメインスレッドで行ったサンプルは下記のようになります。

    DispatchQueue.global(qos: .background).async {
        let ob = Observable<Int>.create { observer -> Disposable in
            for i in 0...10000 {
                if i == 10000 {
                    print("observer called!")
                    observer.onNext(1)
                    observer.onCompleted()
                }
            }
            return Disposables.create()
        }
        ob.observeOn(MainScheduler.instance)
            .subscribe(onNext: { _ in
                print(1)
                for i in 0...100 {
                    if i == 100 {
                        print(2)
                    }
                }
            })
            .disposed(by: self.disposeBag)
        ob.observeOn(MainScheduler.instance)
            .subscribe(onNext: { _ in
                print(3)
            })
            .disposed(by: self.disposeBag)
    }

出力

observer called! // バックグランドスレッド
1 // メインスレッド
2 // メインスレッド
observer called! // バックグランドスレッド
3 // メインスレッド

こちらはバックグランドの処理により、全体の順番が変わることはありますが、基本的にはメインスレッドでの処理は直列処理なので、順番が変わることはありません。

Scheduler の種類

先でもスレッドや処理の実行順などを指定するのに Schedler を使用しましたが、RxSwift では現在下記のような Scheduler が用意されています。

Class 内容
CurrentThreadScheduler (Serial scheduler) Observable.create などで指定されるデフォルトのスケジューラにはこれがしてされていて、スレッドを切り替えず現在のスレッドで処理を実行します。
MainScheduler (Serial scheduler) これは Swift の UI などを更新するためのデフォルトのスレッドで、このスケジューラはメインメソッドから呼ばれた場合はスケジューリングなしですぐに処理が実行されます
SerialDispatchQueueScheduler (Serial scheduler) バックグランドで処理を直列に実行すれ場合に使用するスケジューラで、Qos を引数にとり最適なスレッドを選択することができます。
ConcurrentDispatchQueueScheduler (Concurrent scheduler) バックグランドで処理を並列に実行する場合に使用するスケジューラで、SerialDispatchQueueScheduler と同様に Qos を指定して使用できます。
OperationQueueScheduler (Concurrent scheduler) NSOperationQueue を使った非同期処理を行う時に使用します。また、maxConcurrentOperationCount などで同時実行数を制御したい時などに便利なスケジューラです。

https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Schedulers.md

参考

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

テストについて学習してみた

自動テストについて学んだこと

現在、自動テストについて学習しているので学習したことをまとめていきます。

これから

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

fastlaneのincrement_build_numberがビルドナンバーが取れずにエラーが出る場合の対処方法

プロジェクトのビルドナンバーをインクリメントしたいときに便利なのが、Fastlane Actionのincrement_build_numberですが、なぜか自分の環境だけ、以下の様なエラーが出ました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:22:33]: --- Step: increment_build_number ---

There are multiple projects in this directory.  agvtool does not work with multiple projects in the same directory.

agvtool - Apple-generic versioning tool for Xcode projects
  usage:
    agvtool help
    agvtool what-version | vers [-terse]
    agvtool [-noscm | -usecvs | -usesvn] next-version | bump [-all]
    agvtool [-noscm | -usecvs | -usesvn] new-version [-all] <versNum>
    agvtool [-noscm | -usecvs | -usesvn] tag [-force | -F] [-noupdatecheck | -Q] [-baseurlfortag]
    agvtool what-marketing-version | mvers [-terse | -terse1]
    agvtool [-noscm | -usecvs | -usesvn] new-marketing-version <versString>


[!] Apple Generic Versioning is not enabled in this project.
Before being able to increment and read the version number from your Xcode project, you first need to setup your project properly. Please follow the guide at https://developer.apple.com/library/content/qa/qa1827/_index.html

opps... バージョンナンバーが取れてませんね。

よくみると、以下の様なエラー文言が出ています。

There are multiple projects in this directory. agvtool does not work with multiple projects in the same directory.

avgtoolはプロジェクトディレクトリ直下に複数のプロジェクトがある場合動かなくなるとのこと。

というわけで、プロジェクトディレクトリの中に、他に余計なプロジェクトファイルが紛れてないか確認してみましょう。もしあれば削除してみましょう。

早速、叩いてみると無事実行されました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:28:00]: --- Step: increment_build_number ---

Current version of project *** is: 
    2751

/Users/hoge/iOS/***
[16:28:01]: $ cd /Users/hoge/iOS/*** && agvtool next-version -all && cd -
[16:28:01]: ▸ Setting version of project *** to:
[16:28:01]: ▸ 2752.
[16:28:01]: ▸ Also setting CFBundleVersion key (assuming it exists)
[16:28:01]: ▸ Updating CFBundleVersion in Info.plist(s)...
[16:28:01]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***/SupportingFiles/***-Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***1/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***2/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***3/Info.plist" to 2752
[16:28:02]: ▸ /Users/hoge/iOS/***
[16:28:02]: Result: 2752

同じエラーにお悩みの方はぜひ参考に。( @koiwai2020 さん教えてくれてありがとうございます。)

こんな記事もどうぞ

GoogleスプレッドシートとFastlaneでApp Store説明文を自動更新する方法
https://qiita.com/kurarararara/items/0f15157e93b8b9339656

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

【iOS】fastlaneのincrement_build_numberがビルドナンバーを取れずにエラーが出る場合の対処法

結論から言うと、プロジェクトディレクトリの中に複数のプロジェクトファイルが存在しているとエラーになります。余計なプロジェクトファイルを消せば解決します。

以下は経緯です。


プロジェクトのビルドナンバーをインクリメントしたいときに便利なのが、Fastlane Actionのincrement_build_numberですが、なぜか自分の環境だけ、以下の様なエラーが出ました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:22:33]: --- Step: increment_build_number ---

There are multiple projects in this directory.  agvtool does not work with multiple projects in the same directory.

agvtool - Apple-generic versioning tool for Xcode projects
  usage:
    agvtool help
    agvtool what-version | vers [-terse]
    agvtool [-noscm | -usecvs | -usesvn] next-version | bump [-all]
    agvtool [-noscm | -usecvs | -usesvn] new-version [-all] <versNum>
    agvtool [-noscm | -usecvs | -usesvn] tag [-force | -F] [-noupdatecheck | -Q] [-baseurlfortag]
    agvtool what-marketing-version | mvers [-terse | -terse1]
    agvtool [-noscm | -usecvs | -usesvn] new-marketing-version <versString>


[!] Apple Generic Versioning is not enabled in this project.
Before being able to increment and read the version number from your Xcode project, you first need to setup your project properly. Please follow the guide at https://developer.apple.com/library/content/qa/qa1827/_index.html

opps... バージョンナンバーが取れてませんね。

よくみると、以下の様なエラー文言が出ています。

There are multiple projects in this directory. agvtool does not work with multiple projects in the same directory.

avgtoolはプロジェクトディレクトリ直下に複数のプロジェクトがある場合動かなくなるとのこと。

というわけで、プロジェクトディレクトリの中に、他に余計なプロジェクトファイルが紛れてないか確認してみましょう。もしあれば削除してみましょう。

早速、叩いてみると無事実行されました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:28:00]: --- Step: increment_build_number ---

Current version of project *** is: 
    2751

/Users/hoge/iOS/***
[16:28:01]: $ cd /Users/hoge/iOS/*** && agvtool next-version -all && cd -
[16:28:01]: ▸ Setting version of project *** to:
[16:28:01]: ▸ 2752.
[16:28:01]: ▸ Also setting CFBundleVersion key (assuming it exists)
[16:28:01]: ▸ Updating CFBundleVersion in Info.plist(s)...
[16:28:01]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***/SupportingFiles/***-Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***1/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***2/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***3/Info.plist" to 2752
[16:28:02]: ▸ /Users/hoge/iOS/***
[16:28:02]: Result: 2752

同じエラーにお悩みの方はぜひ参考に。( @koiwai2020 さん教えてくれてありがとうございます。)

こんな記事もどうぞ

GoogleスプレッドシートとFastlaneでApp Store説明文を自動更新する方法
https://qiita.com/kurarararara/items/0f15157e93b8b9339656

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

【iOS】Fastlaneのincrement_build_numberがビルドナンバーを取れずにエラーになる際の対処法

結論から言うと、プロジェクトディレクトリの中に複数のプロジェクトファイルが存在しているとエラーになります。余計なプロジェクトファイルを消せば解決します。

以下は経緯。


プロジェクトのビルドナンバーをインクリメントしたいときに便利なのが、Fastlane Actionのincrement_build_numberですが、なぜか自分の環境だけ、以下の様なエラーが出ました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:22:33]: --- Step: increment_build_number ---

There are multiple projects in this directory.  agvtool does not work with multiple projects in the same directory.

agvtool - Apple-generic versioning tool for Xcode projects
  usage:
    agvtool help
    agvtool what-version | vers [-terse]
    agvtool [-noscm | -usecvs | -usesvn] next-version | bump [-all]
    agvtool [-noscm | -usecvs | -usesvn] new-version [-all] <versNum>
    agvtool [-noscm | -usecvs | -usesvn] tag [-force | -F] [-noupdatecheck | -Q] [-baseurlfortag]
    agvtool what-marketing-version | mvers [-terse | -terse1]
    agvtool [-noscm | -usecvs | -usesvn] new-marketing-version <versString>


[!] Apple Generic Versioning is not enabled in this project.
Before being able to increment and read the version number from your Xcode project, you first need to setup your project properly. Please follow the guide at https://developer.apple.com/library/content/qa/qa1827/_index.html

opps... バージョンナンバーが取れてませんね。

よくみると、以下の様なエラー文言が出ています。

There are multiple projects in this directory. agvtool does not work with multiple projects in the same directory.

avgtoolはプロジェクトディレクトリ直下に複数のプロジェクトがある場合動かなくなるとのこと。

というわけで、プロジェクトディレクトリの中に、他に余計なプロジェクトファイルが紛れてないか確認してみましょう。もしあれば削除してみましょう。

早速、削除した後に叩いてみると無事実行されました。

-> % bundle exec fastlane run increment_build_number
[✔] ? 

[16:28:00]: --- Step: increment_build_number ---

Current version of project *** is: 
    2751

/Users/hoge/iOS/***
[16:28:01]: $ cd /Users/hoge/iOS/*** && agvtool next-version -all && cd -
[16:28:01]: ▸ Setting version of project *** to:
[16:28:01]: ▸ 2752.
[16:28:01]: ▸ Also setting CFBundleVersion key (assuming it exists)
[16:28:01]: ▸ Updating CFBundleVersion in Info.plist(s)...
[16:28:01]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***/SupportingFiles/***-Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***1/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***2/Info.plist" to 2752
[16:28:02]: ▸ Updated CFBundleVersion in "***.xcodeproj/../***3/Info.plist" to 2752
[16:28:02]: ▸ /Users/hoge/iOS/***
[16:28:02]: Result: 2752

同じエラーにお悩みの方はぜひ参考に。( @koiwai2020 さん教えてくれてありがとうございます。)

こんな記事もどうぞ

GoogleスプレッドシートとFastlaneでApp Store説明文を自動更新する方法

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

tableView画面クラッシュ

tableViewの画面がクラッシュする

【エラー】
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.

【対応】
テーブルビューのリロード部分
self.rankingTableView.reloadData()

DispatchQueue.main.async {
self.rankingTableView.reloadData()
}

と囲むとクラッシュしなくなりました。

【参考】
swift初心者がiOS13対応でメインスレッド以外でUI更新をしてクラッシュさせてしまった話
https://qiita.com/rymiyamoto/items/7ace750172b84a2ff809

https://www.it-swarm.dev/ja/ios/gtレイアウトエンジンへの変更は、メインスレッドからアクセスした後、バックグラウンドスレッドから実行しないでください%E3%80%82/811797116/

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

iPhone画面サイズ、ピクセル数早見表

機種名 ハードウェア名 有機ELディスプレイ対応 画面サイズ CSSピクセル デバイスピクセル デバイスピクセル比 アスペクト比 発売初期搭載iOS 最終対応iOS 発売年 メモ
iPhone3G iPhone1,2 3.5 320x480 320x480 1 2:3 iPhoneOS2.0 iOS4.2.1 2008/7/11
iPhone3GS iPhone2,1 3.5 320x480 320x480 1 2:3 iPhoneOS3.0 iOS6.1.6 2009/6/26
iPhone4 iPhone3,1、iPhone3,2、iPhone3,3 3.5 320x480 640x960 2 2:3 iOS4.0 iOS7.1.2 2010/6/24
iPhone4s iPhone4,1 3.5 320x480 640x960 2 2:3 iOS5.0 iOS9.3.5 2011/10/14
iPhone5 iPhone5,1、iPhone5,2 4 320x568 640x1136 2 9:16 iOS6.0 iOS10.3.3 2012/9/21
iPhone5c iPhone5,3、iPhone5,4 4 320x568 640x1136 2 9:16 iOS7.0 iOS10.3.3 2013/9/20
iPhone5s iPhone6,1、iPhone6,2 4 320x568 640x1136 2 9:16 iOS7.0 iOS12.4.8 2013/9/20
iPhone6 iPhone7,2 4.7 375x667 750x1334 2 9:16 iOS8.0 iOS12.4.8 2014/9/19
iPhone6Plus iPhone7,1 5.5 414x736 1080x1920 約2.608 9:16 iOS8.0 iOS12.4.8 2014/9/19
iPhone6s iPhone8,1 4.7 375x667 750x1334 2 9:16 iOS9.0 2015/9/25
iPhone6sPlus iPhone8,2 5.5 414x736 1080x1920 約2.608 9:16 iOS9.0 2015/9/25
iPhoneSE iPhone8,4 4 320x568 640x1136 2 9:16 iOS9.3 2016/3/24
iPhone7 iPhone9,1、iPhone9,3 4.7 375x667 750x1334 2 9:16 iOS10.0 2016/9/25
iPhone7plus iPhone9,2、iPhone9,4 5.5 414x736 1242x2208 3 9:16 iOS10.0 2016/9/25
iPhone8 iPhone10,1、iPhone10,4 4.7 375x667 750x1334 2 9:16 iOS11.0 2017/9/22
iPhone8Plus iPhone10,2、iPhone10,5 5.5 414x736 1242x2208 3 9:16 iOS11.0 2017/9/22
iPhoneX iPhone10,3、iPhone10,6 5.8 375x812 1125x2436 3 約2.17:1 iOS11.0.1 2017/11/3
iPhoneXS iPhone11,2 6.5 375x812 1125x2436 3 約2.16:1 iOS12.0 2018/9/21
iPhoneXS MAX iPhone11,4、iPhone11,6 6.1 414x896 1242x2688 3 約2.16:1 iOS12.0 2018/9/21
iPhoneXR iPhone11,8 6.1 414x896 828x1792 2 約2.16:1 iOS12.0 2018/10/26
iPhone11 iPhone12,1 6.1 414x896 828x1792 2 約2.16:1 iOS13.0 2019/9/20
iPhone11 Pro iPhone12,3 5.8 375x812 1125x2436 3 約2.16:1 iOS13.0 2019/9/20
iPhone11 Pro MAX iPhone12,5 6.5 414x896 1242x2688 3 約2.16:1 iOS13.0 2019/9/20
iPhoneSE(2nd gen) iPhone12,8 4.7 375x667 750x1334 2 9:16 iOS13.4 2020/4/24

※2020/9/11時点ではiOS13.7までアップデート可能

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

[iOS]XcodeのUIButtonを接続した時に、IBOutletになるか、IBActionになるかの条件

超小ネタ
前から疑問だったんですが、UIStoryboardからUIButtonをUIViewControllerなどに接続した時

viewDidLoadより上に持ってきたらIBOutlet
スクリーンショット 2020-09-11 12.11.18.png

viewDidLoadより下に持ってきたらIBAction
スクリーンショット 2020-09-11 12.11.32.png

がデフォルトで表示される模様。
viewDidLoadがない場合はIBOutletになりました

UIViewの場合
awakeFromNibより上に持ってきたらIBOutlet
awakeFromNibより下に持ってきたらIBAction
でした

何だその仕様・・・
つまり

IBOutlet
viewDidLoad or awakeFromNib
IBAction

の順でコードを書こうね! by Apple
ってことですかね?

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

iOS 14:クリップボードにアクセスする前にその内容の種別を検知

iOS 14では、アプリがユーザーのクリップボードのコンテンツにアクセスするたびに、画面上部に警告が表示されます。

アプリがクリップボードのコンテンツにアクセスするのは、その中のコンテンツに気になる点があるかどうか知りたい場合があるためです。

この UIPasteboard.generaldetectPatterns 機能を利用すると、クリップボードのコンテンツが次のカテゴリのいずれかに該当するかどうか検出することができます。

image

  • 数字 .number
  • URL .probableWebURL
  • ウェブ検索クエリー .probableWebSearch

detectPatterns がコンテンツのタイプを判断できない時はいつも .probableWebSearch と報告しているようです。

完了したGithubのデモプロジェクトはこちらから入手できます。

ペーストボードのコンテンツを検出

UIPasteboard.general.detectPatterns(for: [.number, .probableWebSearch, .probableWebURL]) { result in
    switch result {
        case .success(let patterns):
            if patterns.contains(.number) {
                // おそらく数字
            } else if patterns.contains(.probableWebSearch) {
                // おそらくウェブ検索クエリー
            } else if patterns.contains(.probableWebURL) {
                // おそらくURL
            }
        case .failure(let error):
            print(error.localizedDescription)
    }
}

UIPasteboard.general.detectPatterns を使用すると、アラートが表示されません。

興味のあるコンテンツがペーストボードに含まれていることがわかったら、実際のペーストボードのコンテンツにアクセスできます。(たとえば、お使いのアプリが、ペーストボードからURLをコピーすることだけに興味がある場合、まず検出を実行して、URLがペーストボードに含まれていたらペーストボードへアクセスできます)

デモアプリケーション

こちらがGithub上でどのように機能するのかをより良く把握していただけるよう、デモアプリケーションのダウンロードが可能です。

デモアプリケーション内には2つのボタンがあり、パターンを検出 のボタンではペーストボードの内容のタイプが表示され、クリップボードの内容を取得 のボタンではペーストボードの内容がフェッチされます。

⭐️ こちらのウェブページにアクセスすると、私の公開されているQiitaの記事のリストをカテゴリー別にご覧いただけます。

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