カテゴリー
役立ち情報
2023/08/27・役立ち情報
概要
Xcode 15.0 beta 5 以降、こんなエラーが出て CocoaPods の pod installに失敗する。
DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead (in target 'TARGET' from project 'PROJECT')
一応CocoaPodsのリポジトリにIssueが立っており、既に修正がマージされている。しかし、この修正はv1.13.0にマイルストーンが立っており、リリースは進捗的にもう少し先になりそうな様子。それまでは以下の対処法でビルドを通すことができる。
対処法
Podfileを編集する。
# ...
post_install do |installer|
# ...
installer.pods_project.targets.each do |target|
# ...
target.build_configurations.each do |config|
# --- Fix for Xcode 15.0 beta 5 ---
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
# ---------------------------------
end
end
end
参考
"Error 'DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead' in Xcode 15 beta 5" (GitHub)
2023/08/25・役立ち情報
前提
ネイティブからFlutterにURIを渡すためには、EventChannelを利用すると思います。単純にFlutter側でmain()でリスナーを設定し、ネイティブでapplication(_:open:options:)やonNewIntentなどでURIを送り込むと、アプリ起動中にURIが流れてきた場合は動作するしれませんが、URIによってアプリが起動された際にはURIを渡すことができません。何故なら、Flutterエンジンの初期化には時間がかかるためです。
そのため、リッスンされるまではネイティブのコードでURIを貯蔵しておき、リッスンが開始された際に流し込む、というようなアプローチが必要になります。
ホスト(ネイティブ)側のコード
iOS
class UriEventApi: NSObject {
static let channelName = "com.example.app.event/uri" // Channel name (任意に変更)
var channel: FlutterEventChannel
var eventSink: FlutterEventSink? // リッスンが開始されたタイミングで代入され、キャンセルされたタイミングでnilにされる
var pendingUri: String?
init(binaryMessenger: FlutterBinaryMessenger) {
channel = FlutterEventChannel(name: UriEventApi.channelName, binaryMessenger: binaryMessenger)
}
func initHandler() {
channel.setStreamHandler(self)
}
func onUri(uri: String) {
if (eventSink != nil) { // Flutter側で既にリッスンが開始されている場合、
eventSink!(uri) // そのままeventSinkを発火させる
} else { // リッスンが開始されていない = まだFlutterエンジンが初期化されていない 場合、
pendingUri = uri // pendingUriに代入して貯めておく
}
}
}
extension UriEventApi: FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { // リッスンが開始された際、
eventSink = events
if (pendingUri != nil) { // 保留中のURIが存在する場合、
eventSink!(pendingUri) // eventSinkを発火させてFlutter側に伝える
pendingUri = nil
}
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
return nil
}
}
Android
// 実装の内容はiOS(Swift)と同様
class UriEventApi(binaryMessenger: BinaryMessenger) : EventChannel.StreamHandler {
private val channel = EventChannel(binaryMessenger, CHANNEL_NAME)
companion object {
const val CHANNEL_NAME = "com.example.event/uri"
}
private var eventSink: EventChannel.EventSink? = null
private var pendingData: String? = null
fun initHandler() {
channel.setStreamHandler(this)
}
fun onUri(uri: String) {
if (eventSink == null) {
pendingData = uri
} else {
eventSink!!.success(uri)
}
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events
if (pendingData != null) {
eventSink!!.success(pendingData)
pendingData = null
}
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
}
Flutter側のコード
扱いやすくするためにUriEventApiという名前でクラスを作ります。
// lib/event_api/uri_event_api.dart
class UriEventApi {
static const _eventName = "com.example.app.event/uri";
static const _channel = EventChannel(_eventName);
StreamSubscription listen() {
return _channel.receiveBroadcastStream().listen((uri) {
// process
});
}
}
これを任意のタイミングでリッスンします。
UriEventApi().listen();
このコードで基本的には動くはずです。
【Xcode 15/iOS/WidgetKit】"Error (Xcode): Cycle inside Runner; building could produce unreliable results." エラー
navigate_next2023/08/08・役立ち情報
問題発生
久々にFlutterアプリをビルドしようとしたら、以下のようなエラーが発生して失敗する問題が起きた。
Error (Xcode): Cycle inside Runner; building could produce unreliable results.
Cycle details:
→ Target 'Runner': CodeSign
/Users/xxx/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/InstallationBuildProductsLocation/Applications/Runner.app
○ That command depends on command in Target 'Runner': script phase “[CP] Embed Pods Frameworks”
○ Target 'Runner' has copy command from
'/Users/chika/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/BuildProductsPath/Release-iphoneos/WidgetKitExtension.appex' to
'/Users/xxx/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/InstallationBuildProductsLocation/Applications/Runner.app/PlugIns/WidgetKitExtension.appex'
○ That command depends on command in Target 'Runner': script phase “Thin Binary”
○ Target 'Runner' has process command with output
'/Users/xxx/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/InstallationBuildProductsLocation/Applications/Runner.app/Info.plist'
○ Target 'Runner' has copy command from
'/Users/xxx/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/BuildProductsPath/Release-iphoneos/WidgetKitExtension.appex' to
'/Users/xxx/Library/Developer/Xcode/DerivedData/Runner-xxx/Build/Intermediates.noindex/ArchiveIntermediates/Runner/InstallationBuildProductsLocation/Applications/Runner.app/PlugIns/WidgetKitExtension.appex'
環境
Flutter 3.10.6
% flutter --version
Flutter 3.10.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f468f3366c (4 weeks ago) • 2023-07-12 15:19:05 -0700
Engine • revision cdbeda788a
Tools • Dart 3.0.6 • DevTools 2.23.1
Xcode 15.0 Beta 4
対処法
Appleのエラーメッセージは非常に分かりにくいのですが、改めて隅々まで読んでみるとBuild Phasesの順序が問題で、[CP] Embed Pods FrameworksとThin BinaryのBuild Phasesは、WidgetKitのExtensionのコピー作業を前提としているために失敗している、と書かれています。多分。
そのため、Embed App ExtensionsのBuild Phaseを上記の2つより上に持ってくる ことで解決しました。
Build Phasesの内容は環境によってかなり異なるため、上記の画像と違う場合にはエラーメッセージを詳しく読んでみてください。
2023/06/08・役立ち情報
ハマった
以下のように、WindowsホストランナーでOutputしようとするとうまくいかない。
jobs:
build_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- id: test
run: echo "FILENAME=test.zip" >> "$GITHUB_OUTPUT"
- run: echo ${{ steps.test.outputs.FILENAME }}
対処法
WindowsのPowershellにおいては、環境変数にアクセスするにはenv:のプレフィックスが必要な模様。
run: echo "FILENAME=test.zip" >> "${env:GITHUB_OUTPUT}"
2023/06/01・役立ち情報
経緯
Google Search Index用にサイトマップを作りたくなった。サイトマップの生成自体はsitemapパッケージでどうにかなるが、いちいち全部のルートを取得してリストを作るのは面倒臭い。そこで、nitroに備わっているクローラーを使って、サイト内にあるリンクからルート一覧を生成してみる。
nitroのクローラーは、HTMLに含まれる、「/」で始まるhref属性を含むタグを自動的に認識し、リンク先のルートを生成できる。
壁
nitroでは、クローラーによって自動的に動的ルートのページが生成される。しかし、クロール後のルート一覧を取得する方法が存在しない。そこで、ページを生成するタイミングでフックを挟んで、変数のリストに追加していくことにする。
コード
まず、sitemapパッケージをインストールしておく。
yarn add sitemap
次に、nuxt.config.tsのdefineNuxtConfigの上に以下を追加する。
const hostname = "<hostname>"
const routes: string[] = []
export default defineNuxtConfig({
...
続いて、defineNuxtConfigの中に以下を追加する。
export default defineNuxtConfig({
...
nitro: {
hooks: {
"prerender:route"(route) {
routes.push(route.route)
},
close() {
if (routes.length > 0) {
const links: SitemapItemLoose[] = routes.map(route => ({
url: route,
}))
const stream = new SitemapStream({
hostname,
})
return streamToPromise(Readable.from(links).pipe(stream))
.then((sm) => {
return fs.writeFileSync("dist/sitemap.xml", sm.toString())
})
}
},
},
},
...
})
9行目のif (routes.length > 0)は、これを入れないとnuxt prepare(yarn install)を実行した際にもこの処理が走ってしまうために入れています。
2023/03/22・役立ち情報
問題発生
IntelliJ IDEA (というか全Jetbrains IDEで共通) でGitのコミットを署名する設定を、Macでこれに従って設定したところ、以下のようなエラーが出てコミットできなくなった。
error: gpg failed to sign the data
fatal: failed to write commit object
原因
いろいろ調べた結果、パスワード入力用のダイアログの表示に失敗している様子。
GPGではセキュリティーのためか、pinentryというソフトでGUIのパスフレーズ入力ダイアログを出し、コンソールに直接パスフレーズを入力することを避けるようにしている。しかし、macOS版のpinentry-macをインストールすると、pinentryとpinentry-macという2種類のコマンドが実行できるようになる。そして、なんと pinentryの方はmacOSでは動かない。
デフォルトではこのpinentryの方が使用されるようになっているため、エラーが発生している様子。~/.gnupg/gpg-agent.confでpinentryのパスを変更できるらしいが、なぜか設定しても反映されない。(いろいろ試したものの…)
そこで、シンボリックリンクの作成という力技で対処することにする。
対処法
まず、現在設定されているpinentryのパス と、 pinentry-macのパス を調査する。
前者は以下のコマンドで調べられる。
% gpgconf
gpg:OpenPGP:/opt/homebrew/Cellar/gnupg/2.4.0/bin/gpg
...
pinentry:パスフレーズ入力:/opt/homebrew/opt/pinentry/bin/pinentry
上の実行結果でいうと、/opt/homebrew/opt/pinentry/bin/pinentryの部分が現在設定されているパス。そして、このファイルを削除する。
% rm <現在設定されているpinentryのパス>
次に、pinentry-macのパスを調べる。
% which pinentry-mac
/opt/homebrew/bin/pinentry-mac
最後に、以下のコマンドでシンボリックリンクを作成する。
% ln -s <現在設定されているpinentryのパス> <pinentry-macのパス>
これでコミットできるようになるはず。
2023/03/14・役立ち情報
問題発生
OGPのサムネイル生成用に、Puppeteerを使ったCloud Functionsの関数をデプロイしようとした。
開発環境(エミュレーター)では正常に動作するのに、なぜかデプロイするとError: could not handle the requestと表示されて動作しなくなる。ログを確認すると、以下のようなエラーが出ていた。
Error: Could not find Chromium (rev. 1095492). This can occur if either
1. you did not perform an installation before running the script (e.g. `npm install`) or
2. your cache path is incorrectly configured (which is: /www-data-home/.cache/puppeteer).
For (2), check out our guide on configuring puppeteer at https://pptr.dev/guides/configuration.
どうやら「Chromiumどこ…?」というエラーの様子。
結論
以下の手順で解決できる。
functionsディレクトリに.puppeteerrc.cjsファイルを作成し、以下のような中身にする。
/**
* @type {import('puppeteer').Configuration}
*/
module.exports = {
cacheDirectory: require("path").join(__dirname, ".cache", "puppeteer"),
}
firebase.jsonに以下を追記する。(※JSONではコメントの記述はできないため、//以下は削除してください)
"functions": [
{
"source": "functions",
"codebase": "frontend",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log",
".cache" // <= 追記
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
],
package.jsonを以下のように変更する。(※JSONではコメントの記述はできないため、//以下は削除してください)
"scripts": {
"lint": "eslint --ext .js,.ts .",
...
"postinstall": "node node_modules/puppeteer/install.js" // <= 追記
},
...
"engines": {
"node": "16" // <= 16にする。詳細は下記。
}
解説
調べてみると、Puppeteerのv19で以下のような破壊的変更があった模様。
use ~/.cache/puppeteer for browser downloads
(https://github.com/puppeteer/puppeteer/releases/tag/v19.0.0)
ホームディレクトリはCloud Functionsのデプロイの対象にならないため、実際に実行される環境でChromiumが存在せず、エラーとなっているようです。
そのため、Puppeteerのインストール先をプロジェクト内に変更してやります。これが1.です。
この状態でnode_modulesを削除してnpm installし直すと、設定したパスに開発端末のOSに合ったChromiumがインストールされます。ここでfirebase deployしようとすると、「パッケージが大きすぎるためデプロイできません」のようなエラーが発生します。これは巨大なChromium本体がデプロイの対象となっているためで、これはデプロイ不要なので除外してやる必要があります。これが2.です。
あとは、Cloud Functionsではなぜかnpm install時にChromiumが自動的にインストールされないので、postinstallでインストールスクリプトを実行してやります。(3.)
また、Nodeの最新バージョンは18ですが、2023/03/14現在、Node 18用のイメージにPuppeteerの実行に必要なライブラリが含まれていないため、16にします。
2023/01/06・役立ち情報
経緯
GitHub連携を用いて、Cloudflare Pagesでページを公開し、一部のパスでFunctionが実行されるようにしたかったが、なぜかFunctionsだけデプロイされない。
公式ドキュメントの通り、プロジェクトルートにfunctionsディレクトリを作成し、その中にapi/showcase.tsを作り、example.com/api/showcaseでAPIが走るようにしたかった。しかし、そのままデプロイしてもNuxtの静的ページに飛び、Functionsに繋がらない。
対処法
pages functions not recognizedで検索したところ、https://github.com/cloudflare/wrangler2/issues/1859がヒットした。
結論としては、nuxt.config.tsに以下を追記する。
...
nitro: {
preset: "node-server",
},
...
https://github.com/cloudflare/wrangler2/issues/1859#issuecomment-1269616054によると、Nitroでは環境に応じて自動的に適切なプリセットが選択され、Cloudflare Pagesではcloudflare-pagesが設定されるようになります。本来はnuxt generate時に.output/publicに/functionsがコピーされるべきですが、Nuxt3の/serverとして扱われ、認識できるexportが存在しないことからレンダリング前に/functionsが空っぽにされ、結果全くデプロイされないようです。(私の認識が正しければ)
2022/11/19・役立ち情報
問題は起きた。
Jetbrains社のIDE、IntelliJ IDEA / WebStorm でNuxt3を使おうとすると、#importsからインポートしないとメソッドが解決されない。どうやら、VSCodeのプラグインはインポートしなくても解決できるようになっている模様。
これはめちゃくちゃ面倒臭い。
対処法
__imlファイルを編集し、.nuxtディレクトリを処理(インデックス)の対象に含めます__。通常、IntelliJ IDEAでは、「.(ドット)」から始まる隠しディレクトリはGUIでインデックスの対象に含めることができません。なので、プロジェクトの設定ファイルを直接編集します。
[プロジェクトルート]/.idea/[プロジェクト名].imlを開きます。そして、以下を追記します。
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<!-- 略 -->
<content url="file://$MODULE_DIR$/.nuxt" /> <!-- 追記 -->
<!-- 略 -->
</component>
</module>
こうすると、composableなどがimportなしで使えるようになります。
2022/11/03・役立ち情報
問題
タイトルの通り。
対処法
「カスタムVMオプションの編集」から、以下のオプションを追加してMetalを利用したレンダリングを無効化します。こうすることで、ファイルを更新する度に起こる点滅が1回に減ります(ゼロにはなりません)。これで暫定的に対処できます。
-Dsun.java2d.metal=false
追記 (2022/11/25)
IntelliJ IDEA 2022.2.4がリリースされました。リリースノートには修正した旨の記載がありますが、なぜか現在も治っていない模様です。