[Error] The file couldn’t be opened because you don’t have permission to view it.

Symtom

While accessing PHPicker, the following error occurred. However, I could access the image successfully.

2021-03-07 16:28:16.910394+0900 **** [4516:1164237] [default] [ERROR] Could not create a bookmark: NSError: Cocoa 257 “The file couldn’t be opened because you don’t have permission to view it.” }

Solution

Reference:

https://developer.apple.com/forums/thread/654021

https://developer.apple.com/forums/thread/652695

Per Apple Frameworks Developer, they are aware of this issue (64630315) and it will be fixed in a future release. (as of 2020 Summer).

To workaround the permission error for now, you need to reset your simulator / device.

Panasonic Let’s Note CF-SX1 Win10 20H2 Updateで遅くなる

現象

9年前のPanasonic Let’s Note CF-SX1を、ハードディスクをSSDに換装して、さらに Win10 64bit にアップデートしたものをサブPCとして全く問題なく好調に使っていました。

ところが、Windows 10 Version 20H2 更新プログラムを適用したら、めちゃめちゃ遅くなった。タスクマネージャで確認すると、次のように CPU が50%以上システムに消費されており、応答がとても遅くなり、使い物にならないレベルになった。

対応

以前、SSDに換装して、Windows 10を導入した際に、同じ現象になり、その時にLets Noteのドライバーを入れ直して、解決したことを思い出した。

そこで、サポートページより、あたりをつけて、Intel(R) Dynamic Power Performance Management ドライバーを適用すると、正常動作に戻った。

適用後はこんな感じ。

PHPhotoLibrary.requestAuthorization not asking permissions

Sub Title

How to clean up the app and all associated data from devices.

Symptom

XCode 12.4

iPhone 8

iOS 14.4

macOS Big Sur 11.2.1

When I was testing Nega Viewer App Clip which access the camera and photo library, suddenly PHPhotoLibrary.requestAuthorization did not ask permissions. I tried the following cases, and only #3 worked for me.

Solutions

Case #1

  1. Remove the application from the iPhone.
    It did not work.

CASE #2

  1. Remove the application from the iPhone.
  2. Go to [Settings] >> [General] >> [Date & Time]
  3. Set Automatically Off
  4. Set future date

It did not work.

Case #3

  1. Remove the application from the iPhone.
  2. Power off iPhone and restart.

It worked. After the restart, it did not happen again.

Case #4

  1. ⌘ + Shift + 2, or Xcode >> Menu >> Window >> Devices and Simulators
  2. Select the application and press the minus button to delete the app and all associated data.

Before I tried this, it has been fixed. I will try this next time.

App Clipの起動エクスペリエンスをテストする方法

Reference

Testing Your App Clip’s Launch Experience

を翻訳したものです。ご参考になれば幸いです。

概要

TestFlightからApp Clipを起動し、App Clipとその起動エクスペリエンスをテストします。

App Clipのユーザーエクスペリエンスには、高速でスムーズな起動エクスペリエンスが不可欠です。重要な要素の1つは、起動時の起動 URLです。その結果、App Clipエクスペリエンスの構成と、起動URLを処理するコードの記述にかなりの時間を費やすことになります。App Clipがスムーズな起動エクスペリエンスを提供することを確認するには、App Clipとその起動エクスペリエンスをテストする必要があります。App Clipの起動エクスペリエンスは、さまざまな方法でテストできます。

  • App Clipのコードに変更を加えるときは、_XCAppClipURL 環境変数を使用して起動URLを構成します。App Clipをビルドして実行し、起動URLを処理するコードをデバッグします。
  • テストデバイスでローカルエクスペリエンスを作成することにより、App ClipカードのデザインとApp Clipの起動エクスペリエンスを確認します。
  • ベータ版を他のユーザーと共有する準備ができたら、TestFlightでテスター向けのApp Clipエクスペリエンスを作成します。

App Clipをデバッグする

起動 URLはApp Clipのユーザーエクスペリエンスにとって重要であるため、起動URLを処理し、App ClipのUIを更新するコードを徹底的にテストすることが重要です。最初のステップとして、シミュレーターまたはデバイスでApp Clipをビルド、実行、およびデバッグします。次に、App Clipのデバッグ時に起動時にApp Clipで使用できるApp Clipへの起動URLを指定します。

デバッグ用の起動URLを構成するには:

  1. Xcodeで、[Product]> [Scheme]> [Edit Scheme]を選択し、App Clipのスキームを選択します。
  2. [Run]アクションを選択します。
  3. [Arguments]タブで、_XCAppClipURL 環境変数が存在するかどうかを確認します。App Clipターゲットをプロジェクトに追加すると、Xcodeはこの環境変数を追加します。存在しない場合は、環境変数を追加します。
  4. 環境変数の値を、テストする起動URLに設定します。
  5. 変数の横にあるチェックボックスをオンにして、変数を有効にします。
  6. App Clipを作成して実行し、NSUserActivityオブジェクトから構成したテストURLにアクセスします。起動URLへのアクセスの詳細については、「Responding to Invocations」を参照してください。

次のスクリーンショットは、_XCAppClipURL 環境変数の値を使用してApp Clipターゲットの実行アクションを構成するためのシートを示しています。

AppClipターゲットの実行アクションを構成するためのシートを示すスクリーンショット。 _XCAppClipURL環境変数の値は設定されていますが、有効になっていません。

Xcodeを使用してApp Clipをデバッグすると、App Clipがすぐに起動し、App Clipカードは表示されません。App Clipの完全な起動エクスペリエンスをテストおよびテストするときに、起動時にApp Clipカードを確認するには、テストデバイスにローカルエクスペリエンスを登録します。

ローカルエクスペリエンスを登録する

_XCAppClipURL 環境変数を活用すると、起動URLを処理するApp Clipのロジックをデバッグするときに役立ちます。ただし、App Clipが、さまざまな起動からの高速で信頼性の高い起動エクスペリエンスを提供することを確認する必要があります。さらに、App ClipカードはユーザーがApp Clipを起動したときの最初の操作であるため、App Clipカードの画像、テキスト、行動を促すフレーズの動詞を調べることは特に重要です。

起動をテストし、ローカルエクスペリエンスでApp Clipカードのデザインを調べるために、App Clipをウェブサイトに関連付けたり、ビルドをTestFlightにアップロードしたりする必要はありません。

ローカルエクスペリエンスを使用すると、次の方法でApp Clipを起動できます。

  • コントロールセンターのコードスキャナーまたはカメラアプリを使用してApp ClipコードまたはQRコードをスキャンします。
  • デバイスを、App Clipコードと連携したNFC、またはNFCタグに近づけるか、コントロールセンターのNFCタグリーダーでスキャンします。
  • SafariのWebサイトで Smart App バナーをタップします。
  • メッセージアプリで他の人が共有しているリンクをタップします。

ローカルエクスペリエンスを使用してApp Clipの起動エクスペリエンスをテストするには:

  1. テストデバイスでApp Clipのスキームを構築して実行し、App Clipがテストデバイスにキャッシュされていることを確認します。たとえば、Frutaサンプルコードプロジェクトでは、指示に従って構成してから、スキームを実行します。サンプルコードプロジェクトをダウンロードするには、Fruta: Building a Feature-Rich App with SwiftUI.にアクセスしてください。
  2. テストデバイスで、[設定]アプリを開き、[Developer]> [Local Experience]に移動して、[Register Local Experience]を選択します。
  3. テストする起動URLを入力します。場合によっては、https://fruta.example.comのような単純なURLプレフィックスでも可能です。また、パスとクエリパラメータを含むより長い起動URLにすることもできます。
  4. App Clipのbundle IDを入力します。
  5. Title とSubtitle のテキストを入力します。Frutaサンプルコードプロジェクトの場合、タイトル[Order a smoothie.]とサブタイトル[It’s yummy!]を入力します。
  6. 行動を促すフレーズ(たとえばOpen)を選択します。。
  7. App Clipカードのヘッダー画像として使用する写真を選択します。

注意

テストデバイスでApp Clipのスキームを構築して実行し、デバイスにキャッシュするだけでなく、App Clipの.ipaファイルをデバイスに手動で追加することもできます。まず、App Clipの対応する完全なアプリをアーカイブします。次に、Xcodeの[Organizer]ウィンドウで、Ad Hoc またはDevelopment distribution にApp Clipのバイナリをエクスポートします。最後に、App Clipのエクスポートされた.ipaファイルを、Xcodeの[Devices and Simulators]ウィンドウで接続されているデバイスにドラッグします。

次のスクリーンショットは、iPhoneでローカルエクスペリエンスを構成するために使用するインターフェイスを示しています。

ローカルエクスペリエンスを構成するためのインターフェイスを示すiPhoneのスクリーンショット。 URLプレフィックスとしてhttps://example.comを使用し、バンドル識別子としてcom.example.app.clipを使用します。 ローカルエクスペリエンスのアプリクリップカードのタイトルはサンプルアプリ、サブタイトルは「ローカルアプリクリップエクスペリエンス」、行動を促すフレーズの動詞はオープンです。

ローカルエクスペリエンスを使用して起動をテストする

App Clipをテストするためにデバイスでローカルエクスペリエンスを構成したら、起動手段より App Clipを起動できます。ローカルエクスペリエンスでテストする起動に関係なく、上記のようにApp Clipがテストデバイスにキャッシュされていることを確認してください。App Clipがキャッシュされていることを確認するためにApp Clipを少なくとも1回起動したら、次の方法でApp Clipを起動します。

  • 作成したApp Clipコード、QRコード、またはNFCタグをスキャンして、ローカルエクスペリエンスを起動します。テスト用のApp Clip Codesの作成については、「Creating App Clip Codes with the App Clip Code Generator]を参照してください。QRコードを作成したり、NFCタグを記述したりするには、お気に入りのツールを使用します。
  • Webサイトに追加したスマートバナーをタップします。
  • Smart App バナーを表示するウェブサイトへのリンクを共有する。メッセージの送信者をテストデバイスの連絡先として追加したことを確認します。

Associated Domains Entitlementローカルエクスペリエンスを起動するために、App Clipを構成したりWebサイトに関連付けたりする必要はないことに注意してください。Webサイトまたはメッセージアプリからの起動のサポートの詳細については、WebサイトおよびメッセージアプリSupporting Invocations from Your Website and the Messages App.を参照してください。

ヒント

[設定] アプリを開き、[コントロールセンター]を選択して、コードスキャナーとNFCタグリーダーをコントロールセンターに追加します。

デバイスでローカルエクスペリエンスを構成する場合、ローカルエクスペリエンスは、App Store Connect で構成するApp Clipエクスペリエンスよりも優先されます。ただし、ローカルエクスペリエンスでは、Development、Ad Hoc、またはTestFlight配布用に署名されたApp Clipのみが起動されます。App Storeで公開されているApp Clipや完全なアプリを起動することはありません。App Store Connectで構成するApp Clipエクスペリエンスをテストする前に、ローカルエクスペリエンスを削除することを忘れないでください。

TestFlightでテスター向けのApp Clipエクスペリエンスを作成する

信頼できるユーザーエクスペリエンスはApp Clipsにとって非常に重要であり、すべてのユーザーフローとサポートされている起動をテストするために時間を費やす必要があります。App Clipが期待どおりに機能することを確認するために、TestFlightのテスターがApp Clipを利用できるようにすることができます。まず、App Clip付きのアプリをApp Store Connectにアップロードします。次に、App Store Connectで、アップロードされたビルドに移動します。[App Clip]セクションでは、テスト用に最大3つの異なるApp Clipエクスペリエンスを構成できます。

重要

ローカルエクスペリエンスとは異なり、TestFlightでテスター用のApp Clipエクスペリエンスを作成するには、App ClipをWebサイトに関連付ける必要があります。関連するドメインの構成の詳細については、「Associating Your App Clip with Your Website」を参照してください。

ユーザーはApp Clipをインストールせず、App Clipはホーム画面に表示されません。同様に、テスターはApp Clipのベータ版をインストールせず、ホーム画面にも表示されません。代わりに、テスターは、デバイス上のTestFlightを介してテスト用に構成したApp Clipエクスペリエンスを起動します。

App Store Connectでテストするためのエクスペリエンスの構成の詳細については、「Testing an App Clip Experience」を参照してください。

テスターは、TestFlightで配布するApp Clipを起動するようにローカルエクスペリエンスを構成することもできます。ただし、App ClipをWebサイトに関連付ける必要があります。さらに、テスターは、App Store Connectで構成したテストのために、App ClipエクスペリエンスからApp Clipを起動して、App Clipがデバイスにキャッシュされていることを確認する必要があります。

AVCaptureSession.InterruptionReason.videoDeviceNotAvailableInBackground

概要

AVCaptureSession でビデオセッションがバックグラウンド・フォアグラウンドに移行した場合の動作に関して。

Reference

AVCaptureSession.InterruptionReason.videoDeviceNotAvailableInBackground

  • iOS9.0以降
  • Mac Catalyst 13.0+
  • AVFoundation

宣言

case videoDeviceNotAvailableInBackground = 1

Discussion

バックグラウンドでのカメラの使用は禁止されています。バックグラウンドでカメラの実行を開始しようとすると、キャプチャセッションはこの中断理由でAVCaptureSessionWasInterruptedを送信します。stopRunning()メソッドを明示的に呼び出さない場合、startRunning()リクエストは保持され、アプリがフォアグラウンドに戻ると、AVCaptureSessionInterruptionEnded を受信してセッションの実行が開始されます。

解説

AVCaptureがバックグラウンド・バックグラウンドに移行しても、明示的にsession を stopRunning, startRunning を行う必要はなく、自動的に停止・再開される。フォアグランドに戻った時に、AVCaptureSessionInterruptionEnded が呼ばれるので、そこで必要な処理(Permission のチェックなど)を行えば良い。

実装例

    override func viewDidLoad() {
        NotificationCenter.default.addObserver(self, selector: #selector(avCaptureSessionInterruptionEnded), name: Notification.Name.AVCaptureSessionInterruptionEnded, object: nil)
    }

    // フォアグランドに戻って、session が自動的に再開された
    @objc func avCaptureSessionInterruptionEnded() {
        debugPrint("avCaptureSessionInterruptionEnded フォアグラウンドに戻ったので、session が自動的に再開された")
        doSomething()
    }

requestAccess(for:completionHandler:)

オリジナル情報源

このページは、次の情報を日本語に翻訳したものです。お役に立てば幸いです。

requestAccess(for:completionHandler:)

AVFoundation

宣言

必要に応じて、指定されたメディアタイプを記録するためのユーザーの許可を要求します。

class func requestAccess(for mediaType: AVMediaType, 
       completionHandler handler: @escaping (Bool) -> Void)

パラメーター

mediaType

メディアタイプ定数で、videoまたはaudio。メディアタイプが指定されていない場合、invalidArgumentExceptionがスローされます。

handler

許可、または拒否されたときに、1回だけ呼び出されるブロック。

完了ハンドラーは、任意のディスパッチキューで呼び出されます。結果として、UIKit関連の更新がメインキューまたはメインスレッドで呼び出されるようにするのはクライアントの責任です。

ブロックは次のパラメーターを受け取ります。

granted

ユーザーがハードウェアを使用する許可を与えると、true 、それ以外の場合は、falseが返されます。ブロックはすぐに戻ります。

ディスカッション

オーディオまたはビデオの録音には、常にユーザーからの明示的な許可が必要です。許可が必要なメディアタイプのAVCaptureDeviceInputオブジェクトを初めて作成すると、システムは自動的にアラートを表示して記録許可を要求します。または、このメソッドを呼び出して、指定した時間にユーザーにプロンプ​​トを表示することもできます。

重要

アプリは、NSCameraUsageDescriptionまたはNSMicrophoneUsageDescription Info.plist キーを使用してキャプチャデバイスの使用について説明する必要があります。iOSは、最初にユーザーに許可を求めるときにこの説明を表示し、その後、”設定”アプリ に表示します。使用法の説明なしで、このメソッドを呼び出すか、キャプチャセッションを開始しようとすると、例外が発生します。

ユーザーがアクセスを要求されている間、この呼び出しはブロックされず、クライアントは実行を継続できます。アクセスが許可されるまで、メディアタイプのいずれのAVCaptureDevices は、無音オーディオサンプルまたは黒いビデオフレームとなります。ユーザーは、クライアントが最初にアクセスを要求したときにのみ許可を求められます。それ以降の呼び出しでは、ユーザーによって付与されたアクセス許可が使用されます。

ユーザーが録音権限を付与した後、システムは同じアプリで将来使用するための選択を記憶しますが、ユーザーは “設定” アプリを使用していつでもこの選択を変更できます。ユーザーがアプリの再コーディングの許可を拒否した場合、または許可のプロンプトにまだ応答していない場合、音声の録音には無音のみが含まれ、ビデオの録音には黒のフレームのみが含まれます。

このresponseパラメーターは、ユーザーが記録の許可を付与したか拒否したかを示す唯一のパラメーターを持つブロックです。このメソッドは常にすぐに戻ります。ユーザーが以前に記録許可を付与または拒否した場合、呼び出されたときにブロックを実行します。それ以外の場合は、アラートを表示し、ユーザーがアラートに応答した後にのみブロックを実行します。

audioで、このAVAudioSessionメソッドの呼び出しは、requestRecordPermission(_:) メソッドの呼び出しと同じです。

AppClipの対応するアプリでデータを利用できるようにする

リファレンス

このページは、Making Data Available to the App Clip’s Corresponding App を翻訳したものです。

Overview

App Groups 機能を追加し、App Clipに保存されているデータを、対応するアプリで利用できるようにする。

App Clip のユーザーがフルアプリをインストールすると、アプリクリップがフルアプリに置き換わり、すべての呼び出しを受け取ります。アプリクリップに慣れ、フルアプリを使い始めたユーザーに優、れたユーザーエクスペリエンスを提供することは重要です。

例えば:

  • App Clip を使用したときに入力した情報を、アプリで表示します。
  • ダウンロードしたデータをアプリで利用できるようにします。
  • ユーザーは、自分のアカウントに再度ログインする必要がない。

アプリクリップとアプリ間でデータを共有する

App Clip は、共有コンテナにデータを保存することで、対応するアプリがデータにアクセスできるようにすることができます。フルアプリは、App Clip を置き換えるときにこのデータにアクセスすることにより、優れたユーザーエクスペリエンスを提供できます。

共有コンテナにデータを保存するには:

  1. App Clip とフルアプリの両方のターゲットにApp Groups 機能を追加します。
  2. 両方のターゲットに対して、同じ app group capability (たとえば、group.exampleApp.appClipMigration) に追加します。
  3. App Clipのコードで、containerURL(forSecurityApplicationGroupIdentifier:)を使用して共有コンテナーのURLを取得し、そのURLを使用してデータを保存します(たとえばwrite(to:atomically:encoding:))。
  4. アプリのコードで、同じ関数を使用して共有コンテナーのURLを取得し、そのコンテンツにアクセスします。たとえば、init(contentsOf:)を使用します。

共有コンテナに加えて、App Clipは、フルアプリにアクセスできる共有されたUserDefaultsインスタンスに情報を保存することもできます。次のコードは、構成された app group を使用して共有 UserDefaults インスタンスを作成し、文字列を格納します。

guard let sharedUserDefaults = UserDefaults(suiteName: "group.exampleApp.appClipMigration") else {
    // Error handling
}
sharedUserDefaults.set("A sample string", forKey: "sharedText")

ユーザーがフルアプリをインストールすると、共有の user defaults にアクセスできます。たとえば、前のコードに格納されている文字列にアクセスするには、次のようにします。

guard let sharedUserDefaults = UserDefaults(suiteName: "group.exampleApp.appClipDataMigration") else {
    // Error handling}
guard let migratedData = sharedUserDefaults.string(forKey: "sharedText") else { return }

アプリと App Clip 間でユーザーのプライバシーを保護するために、App Clip は対応するアプリとのみデータを共有できます。さらに、App Clip がキーチェーンに保存している情報に、対応するフルアプリからアクセスできるようにすることはできません。

注意

従来の UserDefaults.standard を使用した場合、App Clip とフルアプリ間でのデータ共有はできず、nil が返される。

重要

パスワードなどの機密性の高いユーザー情報を、共有アプリのコンテナーや user defaults に保存しないでください。

Appleでサインイン を推奨

App Clips は、ユーザーが自分のアカウントにサインインしたり、アカウントを作成したりできるようにするために、任意のテクノロジーを使用できます。ただし、App Clipでユーザーがアカウントにログインする必要がある場合は、Appleでサインイン を検討してください。シンプルで安全なプライバシー保護のアカウント作成とサインインエクスペリエンスを提供するだけでなく、ユーザーがフルアプリを使い始めたときにポジティブなエクスペリエンスを提供します。

Appleでサインイン を提供し、共有データコンテナーを作成する場合は、ユーザーがフルアプリの使用を開始するときに再度ログインする必要がないユーザーエクスペリエンスを作成します。次のコードは、ASAuthorizationAppleIDCredentialを共有UserDefaultsインスタンスに格納します。

let groupUserDefaults = UserDefaults(suiteName: "group.com.example.appClipDataMigration")
guard let credential = authorization.credential as ASAuthorizationAppleIDCredential else { return }
groupUserDefaults?.set(credential.user, forKey: "SavedUserID")

アプリのコードで、保存されているApple ID認証資格情報を取得し、それを使用してユーザーにサインインします。

let provider = ASAuthorizationAppleIDProvider()
let groupUserDefaults = UserDefaults(suiteName: "group.com.example.appClipDataMigration")
let user = groupUserDefaults?.get("SavedUserID")
provider.getCredentialState(forUserID: user) { state, error in
    if state == .authorized {
        readFavoriteSmoothies(user)
    }
}

Fix UIImage orientation

References

stackoverflow

Background

It seems UIImage orientation get wrong in some cases.

Solution

import UIKit

extension UIImage {
    
    // Fix UIImage Orientation 
    func fixOrientation() -> UIImage {
        if self.imageOrientation == .up { return self }
        
        UIGraphicsBeginImageContext(self.size)
        self.draw(at: .zero)
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

error: The UIDeviceFamily of an App Clip (‘[1, 2]’) must be equal to the UIDeviceFamily of its containing parent app (‘[1]’).

Error Message

error: The UIDeviceFamily of an App Clip (‘[1, 2]’) must be equal to the UIDeviceFamily of its containing parent app (‘[1]’).

Cause

Supported device type of the original application and the App Clip are different.

Original Application
App Clip

Solution

Set same supported device type(s).

Apple should use more appropriate error message!

Error: Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.’

Symtom

While adding App Clip target to the original target, the following uncaught exception occurred. As for the normal app target works fine. Only on App Clip Target did not work.

2021-02-23 05:48:05.968176+0900 NegaViewerAppClip[7319:368124] [Storyboard] Unknown class _TtC10NegaViewer18MainViewController in Interface Builder file.

2021-02-23 05:48:06.032046+0900 NegaViewerAppClip[7319:368124] *** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<UIViewController 0x7f93b5e20ac0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key imageViewOut.’

*** First throw call stack:

(

0   CoreFoundation                      0x00007fff20421af6 __exceptionPreprocess + 242

)

libc++abi.dylib: terminating with uncaught exception of type NSException

*** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<UIViewController 0x7f93b5e20ac0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key imageViewOut.’

terminating with uncaught exception of type NSException

CoreSimulator 732.18.6 – Device: iPhone 12 Pro Max (31C0DAD9-EF0A-4822-A496-75B919FEB25E) – Runtime: iOS 14.4 (18D46) – DeviceType: iPhone 12 Pro Max

(lldb) 

Cause

It happened while I was refactoring (renaming) the class.

In the main storyboard, the Module of MainViewController was specified the original application target. So, MainViewController is bundled only for the original application target.

Solution

Check “Inherit Module From Target” as follows and it worked.