ObjecTips

基本Objective-Cで iOS とか OS X とか

iOS 11 での UIImagePickerController の変更点

iOS 10 までは UIImagePickerControllerUIImagePickerControllerSourceTypePhotoLibrary の組み合わせでフォトライブラリを表示する際にアクセス権限の確認を自動で行ってくれていた。
アクセス権限がなければ鍵マークが表示され一切の情報を取得する事ができない。ユーザの操作としては必ずキャンセルが行われる。*1

iOS 10

自動でアクセス許可のアラートが表示される
f:id:Koze:20170920044429p:plain

権限なし、キャンセルしか出来ない
f:id:Koze:20170920044433p:plain

権限があればフォトライブラリを表示、写真を選択可能
f:id:Koze:20170920044437p:plain

iOS 11 ではアクセス権限の確認が自動で行われない様になった。そして権限がなくともフォトライブラリの表示は行われ、写真の選択も出来てしまう。

iOS 11

権限の確認無しにいきなりフォトライブラリが表示
f:id:Koze:20170920044959p:plain

何が起こるか

写真の選択を行うと通常通り delegate メソッドが呼ばれる。

- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;

この時 info には
UIImagePickerControllerMediaType
UIImagePickerControllerOriginalImage
UIImagePickerControllerReferenceURL
UIImagePickerControllerImageURL
といった情報が入っている。

UIImagePickerControllerReferenceURL には ALAsset のURLが入っている。
iOS 11 で deprecated になったがまだ使う事は出来る。

ex.)
assets-library://asset/asset.HEIC?id=97F34D03-1230-4F4E-9AA6-E7A7469158DE&ext=HEIC

フォトライブラリへのアクセスが許可されていれば以下の様にしてアセットへアクセスする事が出来る。

    NSURL *assetURL = info[UIImagePickerControllerReferenceURL];
    PHFetchResult<PHAsset *> *result = [PHAsset fetchAssetsWithALAssetURLs:@[assetURL] options:nil];
    PHAsset *asset = result.firstObject;
    NSLog(@"%@ %@", result, asset);

ただし、iOS 11 の場合でアクセス権限無しでフォトライブラリを表示選択してこのコードが実行された場合 PHFetchResultPHUnauthorizedFetchResult という Private クラスが返され、アセットの取得が出来ない。

アセットの取得には失敗するがアセットにアクセスしようとした事がトリガーとなってアクセス許可のアラートが表示される。

f:id:Koze:20170920052745p:plain

このタイミングのアラートでアクセス権限を得たとしても既にアセットのフェッチには失敗しているので、再度フォトライブラリを開くか assetURL を使って再度アセットをフェッチするなどしないとアセットは取得出来ない。

iOS 11 に対応する処理の流れとしては UIImagePickerController を表示する前かアセットをフェッチする前に PHPhotoLibrary

+ (void)requestAuthorization:(void(^)(PHAuthorizationStatus status))handler;

で権限を得ておく必要がある。

iOS 10 まではそもそもアクセスが許可されていないとフォトライブラリが表示されないので、こういった事は問題は起こり得ない。

アクセス権限無しでフォトライブラリを開ける意味

アクセス権限が無い状態でも UIImagePickerControllerOriginalImage で取得した UIImage は利用する事が出来る。
また UIImagePickerControllerImageURL を使って NSURL から UIImageNSData を作ったりファイルコピーなどする事も出来る。

UIImagePickerControllerImageURL は iOS 11 で登場したもので元画像のファイルURLが入っている。

ex.)
file:///private/var/mobile/Containers/Data/Application/1770CD90-C268-48BF-8A35-6210698472EF/tmp/EBD7BD90-2A50-4BEB-86D3-63ADB1E12CB0.jpeg


先の例だとアクセス権限が無い状態 = 未確認、つまり PHAuthorizationStatusNotDetermined でのケースだったが、アクセス許可のアラートでアクセスが拒否された場合、つまり PHAuthorizationStatusDenied の状態でも UIImagePickerController によってフォトライブラリを開く事が出来る。

画像に様に許可アラートや設定画面でアクセスが許可されていなくてもフォトライブラリの表示と利用が可能

f:id:Koze:20170920060545p:plain f:id:Koze:20170920060548p:plain

まとめ

  • iOS 11 では UIImagePickerController によるフォトライブラリの表示はユーザのアクセス権限が不要になった。
  • アクセス権限が無い場合でも UIImage や画像の NSURL を使ってユーザが選択した画像データを取り扱う事が出来る。
  • フォトライブラリへのアクセスが発生する PHAsset の取得はアクセス権限が必要。

UIImagePickerController はユーザが操作しているのでOK(許可されていると同義)、PHAsset の取得などアプリがコードでフォトライブラリへアクセスする場合はユーザには何をしているか見えないのでアクセス権限が必要という方針なんだろうと推察される。

※動作確認 iOS 11.0 Xcode 9

iOS 11 UIImagePicker test

*1:Info.plist に NSPhotoLibraryUsageDescription は記載済みの前提

Core Dataのデータベースの保存場所を切り替える

保存場所を固定的に変更する方法は前回の記事を参照

koze.hatenablog.jp

今回の保存場所の切り替えというのは既に使用している A.sqlite ファイルから別の B.sqlite を使用するようにしたりまた A.sqlite に戻したりするという意味での動的な切り替え。
利用ケースの想定としては

  • ローカルDBとクラウド上のデータのローカルキャッシュのDBを分けて管理する *1
  • サービスのアカウント毎にローカルキャッシュのDBファイルを切り替える *2
  • ローカルデータを削除せず、テストデータを読み込ませてまた後にローカルデータへ戻す *3

等々。

実装

実装はシンプル。Xcode のテンプレートに沿って AppDelegate への実装とする。
まず NSPersistentContainer の管理クラス(ここでは AppDelegate)にDBのURLを設定出来るようにする。

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (readonly, strong) NSPersistentContainer *persistentContainer;
@property (strong) NSURL *databaseURL; // <-- here

- (void)saveContext;

@end

databaseURL が設定されたら値を保持して、persistentContainer を nil にする。

- (void)setDatabaseURL:(NSURL *)databaseURL
{
    _databaseURL = databaseURL;
    _persistentContainer = nil;
}

persistentContainer の初期化コードにこの databaseURL を使って初期化するよう仕込む。
初期化処理は次に persistentContainer メソッドが呼ばれる時に実行される。

- (NSPersistentContainer *)persistentContainer {
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
            if (self.databaseURL) {
                NSPersistentStoreDescription *storeDescription = _persistentContainer.persistentStoreDescriptions.firstObject;
                storeDescription.URL = self.databaseURL;
            }
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

以下の様に databaseURL を変更した後に persistentContainer へのアクセスが発生すると指定の場所へDBが作られる事になる。

    NSURL *databaseURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"Test.sqlite"];
    appDelegate.databaseURL = databaseURL;

もしかするとテンプレートの persistentContainer メソッドの様に _persistentContainer が nil の場合に初期化処理が走るのではなく、初期化処理をメソッドとして用意してやって、databaseURL の設定と共に初期化処理を明示的に実行させてやっても良いかも知れない。

サンプルコードの全体は以下。
(その他の AppDelegate の処理は割愛して Core Data の実装のみ記述)

Change PersistentContainer URL dynamically.

*1:例えばメモアプリのローカルデータとiCloud上のデータ

*2:DBの作りで1つのDBファイルで複数アカウントを管理する事も可能ではある

*3:開発中の話なので前回の方法+マクロで切り替えとかでも可能

NSPersistentStoreDescription のデフォルト値とカスタマイズ

iOS 10で導入された NSPersistentContainer を使うとCore Dataのセットアップのコードがずいぶん短くなる。Xcode のテンプレートはこんな感じ

- (NSPersistentContainer *)persistentContainer {
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

これだけで済んでしまう。
でも以前セットアップで使ってた NSMigratePersistentStoresAutomaticallyOption NSInferMappingModelAutomaticallyOption NSSQLitePragmasOption といったオプション設定はどうなっているの?と疑問に思ったので調査。

設定内容は _persistentContainer.persistentStoreDescriptions で取得できる NSPersistentStoreDescription のインスタンスから参照出来るらしい。
このインスタンスから取得可能なプロパティを調べてみると以下の様になった。

property value
type SQLite
configuration null
URL ~/Library/Application Support/<Name>.sqlite
options {
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
}
readOnly NO
timeout 240
sqlitePragmas {
}
shouldAddStoreAsynchronously NO
shouldMigrateStoreAutomatically YES
shouldInferMappingModelAutomatically YES

設定をカスタマイズする

loadPersistentStoresWithCompletionHandler: を呼ぶ前に NSPersistentStoreDescription の各値を設定する事でカスタマイズが出来る。

例えば URL の様に readwrite なプロパティはそのまま素直に変更可能

_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
NSPersistentStoreDescription *storeDescription = _persistentContainer.persistentStoreDescriptions.firstObject;
NSURL *URL = [storeDescription.URL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"NewLocation.sqlite"];
storeDescription.URL = URL;
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {

optionssqlitePragmas は readonly なプロパティになっている代わりに以下の値設定用のメソッドが用意されているのでこれを使う。

- (void)setOption:(nullable NSObject *)option forKey:(NSString *)key;
- (void)setValue:(nullable NSObject *)value forPragmaNamed:(NSString *)name;

例えば以下で SQLite の journal_mode を shm とwal ファイルを生成しない DELETE モードに設定する事が出来る。

_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
NSPersistentStoreDescription *storeDescription = _persistentContainer.persistentStoreDescriptions.firstObject;
[storeDescription setValue:@"DELETE" forPragmaNamed:@"journal_mode"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {

これらのメソッドを使えばCore Dataのセットアップが従来通り柔軟に出来そうな目処が立つ。

ちなみに shouldMigrateStoreAutomaticallyshouldInferMappingModelAutomaticallyNO にすると optionsNSInferMappingModelAutomaticallyOptionNSMigratePersistentStoresAutomaticallyOptionNO になっていた。
おそらくこれらは良く使うオプションなのでプロパティのフラグ設定と自動で連携して簡単に options にキーと値を設定してくれる様になっているらしい。


参考 SQLite の pragma のリスト
https://sqlite.org/pragma.html

Xcode 9 で無線で開発 Wireless Debugging

WWDC 2017 にて Xcode 9 ではUSB接続無しで無線で繋いで開発出来るって話が出ていたけど、何の気なしに Xcode 9 を起動しても同じLAN内の iPhone がビルドターゲットに出てこないので調べてみたらすぐにやり方が分かった。

WWDC 2017 Session 404 の6分20秒あたり
Debugging with Xcode 9 - WWDC 2017 - Videos - Apple Developer

Xcode のメニュー
Window > Devices and Simulators でデバイスウィンドを開いてデバイス一覧からデバイスを選択すると Connect via network というチェックボックスがあるのでそれをチェック。*1
このチェック項目が表示されるのはiOS 11以降の端末のみである事と、チェック作業の際には端末とMacをUSB接続しておく必要がある事に注意。

チェックすると端末のUSB接続を抜いてもデバイス一覧に表示されたままになって、Xcode のビルドターゲットにも端末が表示されるようになるので後は普段通りに開発可能になる。
ワイヤレスで開発出来るのでAPI的にはARKit、Core Motion、AVFoundationなど、ジャンルとしてはAR、ヘルスケア、スポーツ、カメラ系の端末を持って動かす類のアプリの開発が便利になりそう。

*1:β版のためスクショ無し

PDFView の表示設定まとめ

関連記事

koze.hatenablog.jp

※ iOS 11 beta 時点での検証です

検証コード
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    PDFView *view = [[PDFView alloc] initWithFrame:self.view.bounds];
    view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    view.autoScales = YES;
    view.displayDirection = kPDFDisplayDirectionVertical;
    view.displayMode = kPDFDisplaySinglePage;
    view.displaysRTL = NO;
    [self.view addSubview:view];
    
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf"];
    PDFDocument *document = [[PDFDocument alloc] initWithURL:URL];
    view.document = document;
}
検証結果

画像の数字は表示ページ数
白背景は初期状態で表示される部分、灰色部分はスクロール領域の部分

displaysRTL = NO

kPDFDisplayDirectionVertical kPDFDisplayDirectionHorizontal
kPDFDisplaySinglePage f:id:Koze:20170612202100p:plain f:id:Koze:20170612202100p:plain
kPDFDisplaySinglePageContinuous f:id:Koze:20170612202439p:plain f:id:Koze:20170612203519p:plain
kPDFDisplayTwoUp f:id:Koze:20170612202611p:plain f:id:Koze:20170612202611p:plain
kPDFDisplayTwoUpContinuous f:id:Koze:20170612202724p:plain f:id:Koze:20170612202724p:plain

displaysRTL = YES

kPDFDisplayDirectionVertical kPDFDisplayDirectionHorizontal
kPDFDisplaySinglePage f:id:Koze:20170612202100p:plain f:id:Koze:20170612202100p:plain
kPDFDisplaySinglePageContinuous f:id:Koze:20170612202439p:plain f:id:Koze:20170612203519p:plain
kPDFDisplayTwoUp f:id:Koze:20170612204616p:plain f:id:Koze:20170612204616p:plain
kPDFDisplayTwoUpContinuous f:id:Koze:20170612204920p:plain f:id:Koze:20170612204920p:plain

以下の2点がポイント

  • displaysRTL の設定は kPDFDisplayTwoUpkPDFDisplayTwoUpContinuous の時のみ効果有りで見開きの左右を指定するパラメータ
  • kPDFDisplayTwoUpContinuous にしていると kPDFDisplayDirectionHorizontal の設定は効果無し。見開きのまま横スクロールは出来ない。(仮に横スクロール出来たしたとしても kPDFDisplaySinglePageContinuous と見た目が変わらないため?)

という事で、ページ送り(スクロール)自体を右から左にしてくれるオプションは無いようなので日本の小説や漫画など縦書きのコンテンツを表示させる場合は Continuous モードを使わないで自前でページ移動を実装する必要がある。

Vision Framework で水平角検出(傾き) Horizon Detection

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
iOS 11 Core Image の変更点 - ObjecTips
koze.hatenablog.jp

Horizon Detection

Vision Framework を使って傾き検出と得られた結果から表示の補正を行う。
カメラロールの中に程良く傾いた景色の画像があったのでそれを使う。まず何も処理を入れず普通に UIImage を作成して UIImageView に配置する。

f:id:Koze:20170608035929p:plain

実装

Horizon Detection with Vision Framework

まず VNImageRequestHandler を作成。
今回は UIImage 経由で CGImageRef から作成している。
次に VNDetectHorizonRequest 作成しハンドラを設定してリクエストを実行する。

うまく傾きを検出できると request.resultsVNHorizonObservationが入ってくる。
VNHorizonObservation の持つプロパティは以下2つ

@property (readonly, nonatomic, assign) CGAffineTransform transform;
@property (readonly, nonatomic, assign) CGFloat angle;

ドキュメントにもヘッダにも何も書かれていないけど、angle は検出された傾きだろうという事が分かる。ちなみにこの値は radian になっている。 この angle から以下の様に UIImageView を補正表示するための CGAffineTransform を作成する。

CGAffineTransform t = CGAffineTransformMakeRotation(-horizonObservation.angle);

これまで同様に座標系が逆になっているので結果の angle にマイナス値をかけて値を補正している。
この CGAffineTransformUIImageView に設定してやる。

実行結果

以下の様になる。

f:id:Koze:20170608040856p:plain

正しく傾きの補正が出来ている模様。
UIImageView の中心をずらさずに回転のみで補正した形になる。

コメントアウト部分にある様に

CGAffineTransformInvert(CGAffineTransformMakeRotation(horizonObservation.angle));

この様に値を補正してやっても同じ結果になる。

実行結果2

検出結果の VNHorizonObservation にもう1つプロパティがある。

@property (readonly, nonatomic, assign) CGAffineTransform transform;

これを使って UIImageView を変形してみる。
検出結果の座標系は逆になっているので先の例と同じ様に Invert で補正してやる。

CGAffineTransformInvert(horizonObservation.transform);

結果は以下

f:id:Koze:20170608041443p:plain

傾きの補正はOKっぽい。
ただ位置の変形がこれでいいのかどうか、、?
transform プロパティが何を表しているのか、どういった値が入ってくるのか現時点ではドキュメントも無いしWWDCのセッションもまだなので何かしら解説が出るのを待ってみたい。

iOS 11 Core Image の変更点

iOS 11 関連記事
iOS 11 UIKit の変更点 - ObjecTips
iOS 11 Foundation の変更点 - ObjecTips
iOS 11 Messages Framework の変更点 - ObjecTips
iOS 11 PDFKit - ObjecTips
Vision framework でテキスト検出 TextDetection - ObjecTips
Vision Framework で水平角検出(傾き) Horizon Detection - ObjecTips

Core Image

CIImage - Core Image | Apple Developer Documentation

CIImage
@property (nonatomic, readonly, nullable) AVDepthData *depthData NS_AVAILABLE(10_13,11_0);

Depthデータの取得。
イニシャライザのオプションで kCIImageAuxiliaryDepthkCIImageAuxiliaryDisparity のどちらかを YES に設定すると取得出来るらしい。このオプションを用いるとプライマリの画像の代わりに補助(auxiliary)画像の CIImage が作られて、もし補助画像が無い場合は nil になってしまうらしい。
補助画像は half float のモノクロ画像との事。

/* Returns a new image by changing the receiver's sample mode to bilinear interpolation. */
- (CIImage *)imageBySamplingLinear NS_AVAILABLE(10_13, 11_0)
  NS_SWIFT_NAME(samplingLinear());

/* Returns a new image by changing the receiver's sample mode to nearest neighbor. */
- (CIImage *)imageBySamplingNearest NS_AVAILABLE(10_13, 11_0)
  NS_SWIFT_NAME(samplingNearest());

画像の補間モードをバイリニア補間(bilinear interpolation)とニアレストネイバー(nearest neighbor)という補間方法に変換してくれるらしい。

CIFIlter

iOS 11 で追加された CIFIlter

CIAreaMinMaxRed
CIAttributedTextImageGenerator
CIBarcodeGenerator
CIBicubicScaleTransform
CIBokehBlur
CIColorCubesMixedWithMask
CIColorCurves
CIDepthBlurEffect
CIDepthToDisparity
CIDisparityToDepth
CIEdgePreserveUpsampleFilter
CILabDeltaE
CITextImageGenerator

DepthToDisparity と DepthToDisparity が存在していて Depth と Disparity の相互変換が出来るという事が分かる。

CIContext
- (nullable NSData*) PNGRepresentationOfImage:(CIImage*)image
                                       format:(CIFormat)format
                                   colorSpace:(CGColorSpaceRef)colorSpace
                                      options:(NSDictionary*)options NS_AVAILABLE(10_13,11_0);
- (nullable NSData*) HEIFRepresentationOfImage:(CIImage*)image
                                        format:(CIFormat)format
                                    colorSpace:(CGColorSpaceRef)colorSpace
                                       options:(NSDictionary*)options NS_AVAILABLE(10_13,11_0);
- (BOOL) writePNGRepresentationOfImage:(CIImage*)image
                                 toURL:(NSURL*)url
                                format:(CIFormat)format
                            colorSpace:(CGColorSpaceRef)colorSpace
                               options:(NSDictionary*)options
                                 error:(NSError **)errorPtr NS_AVAILABLE(10_13,11_0);
- (BOOL) writeHEIFRepresentationOfImage:(CIImage*)image
                                  toURL:(NSURL*)url
                                 format:(CIFormat)format
                             colorSpace:(CGColorSpaceRef)colorSpace
                                options:(NSDictionary*)options
                                  error:(NSError **)errorPtr NS_AVAILABLE(10_12,10_0);

これまではTIFFとJPEGに書き出す事が可能だったのが、PNGとHEIFへの書き出しも可能になった。

CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationAVDepthData NS_AVAILABLE(10_13,11_0);
CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationDepthImage NS_AVAILABLE(10_13,11_0);
CORE_IMAGE_EXPORT NSString * const kCIImageRepresentationDisparityImage NS_AVAILABLE(10_13,11_0);

上記の書き出しメソッドにオプションとして指定する事が出来る。ただしヘッダによればTIFFとPNGはオプション指定が不可で、JPEGとHEIFの場合にこれらのオプションが指定可能らしい。

CIRenderTask, CIRenderDestination, CIRenderInfo

非同期レンダリングのためのクラス群 CIRenderTask には

- (nullable CIRenderInfo*) waitUntilCompletedAndReturnError:(NSError**)error;

というメソッドがあるので同期的にも使えそう。

CIKernel
+ (nullable instancetype)kernelWithFunctionName:(NSString *)name
                           fromMetalLibraryData:(NSData *)data
                                          error:(NSError **)error NS_AVAILABLE(10_13, 11_0);

+ (nullable instancetype)kernelWithFunctionName:(NSString *)name
                           fromMetalLibraryData:(NSData *)data
                              outputPixelFormat:(CIFormat)format
                                          error:(NSError **)error NS_AVAILABLE(10_13, 11_0);

data には Core Image Standard Library でコンパイルされた mtallib ファイルのデータ、name には Metal Shading Language で書かれた関数名が必要らしい。

CIBlendKernel

2つの画像をブレンドする CIColorKernel のプリセット群の様なものが用意された。
例えば

@property (class, strong, readonly) CIBlendKernel *componentAdd;
@property (class, strong, readonly) CIBlendKernel *componentMultiply;

などクラスプロパティで簡易的にカーネル処理の色フィルタを呼び出す事が出来る。その数50個

CIBarcodeDescriptor

バーコードデータのモデルクラスでサブクラスとして以下が存在する。

  • CIQRCodeDescriptor
  • CIAztecCodeDescriptor
  • CIPDF417CodeDescriptor
  • CIDataMatrixCodeDescriptor`

バーコードの生成と、Core Image, Vision, AVFoundation でのバーコードの検出で使用されるとの事。
これまではカメラ画像からは AVFoundation を使って色々なバーコードを検出する事が出来たけど、静止画像からのバーコード検出は Core Image の CIFeature のサブクラスとして存在している CIQRCodeFeature でしか出来なかった。
iOS 11 では QRCode 以外の上記のバーコードも静止画からの検出が可能になるはず。

CIQRCodeFeature
@property (nullable, readonly) CIQRCodeDescriptor *symbolDescriptor NS_AVAILABLE(10_13, 11_0);

前述のバーコードのモデルクラスを取得可能。

感想

CIBlendKernel のプリセット呼び出しは簡単だけど CIFilter のカラーブレンド系とパフォーマンスが違うのか気になる。呼び出し型が違うだけで中の処理は同じではないか?とも思う。
HEIFデータの扱いとか AVDepthData の活用とかはしらばくは2眼カメラの iPhone 7 Plus に限定されてしまうかも知れないけど面白そう。このあたりの詳細は AVFoundation を調べる必要がありそう。