Skip to content

0.2.0 - #6

Merged
wf9a5m75 merged 59 commits into
mainfrom
0.2.0-2
Aug 16, 2026
Merged

0.2.0#6
wf9a5m75 merged 59 commits into
mainfrom
0.2.0-2

Conversation

@wf9a5m75

Copy link
Copy Markdown
Contributor

react-sdk 0.2.0。

変更

  • 全パッケージを 0.2.0 に統一。内部依存レンジを ^0.2.0 へ。
  • licenseApache2(不正な SPDX)から Apache-2.0 へ修正。
  • react-geojson-layerreact-geojson にリネーム(npm 名 / ディレクトリ / submodule / api ベースライン / examples / docs)。GeoJSONLayer などの公開 API 名は変更なし。
  • 地図 SDK 依存を宣言レンジ内の最新へ。実際に動いたのは maplibre-gl 6.3.0→6.4.0 と @turf/turf 7.3.5→7.4.0 のみ。react-for-tomtommaplibre-gl は 5.x 据え置き(@tomtom-org/maps-sdk^5.24.0 依存)。

リリース経路の修正

いずれも publish 前の検証で見つけたもの。

  • react-for-mapkit@0.1.3 は npm 上で中身が空だった。build:packages に唯一登録されておらず、ビルドされないまま files: ["dist"] が何にもマッチせず公開されていた。0.2.0 で解消。
  • react-for-mappls が親リポジトリに 1 ファイルも追跡されていなかった。 公開対象なのに CI のチェックアウトに存在しなかった。submodule として登録(react-icons-{jp,us,weather} も同様)。
  • .gitignore*.mjsscripts/ を飲んでいた。 新規スクリプトが git add -A で無言のまま無視されていた。!scripts/*.mjs を追加。
  • npm-publish の manifest 復元が submodule しか見ていなかった。 submodule でない react-for-mappls / react-kml"*" 依存が復元されずに publish される経路だった。scripts/unlink-local-workspaces.mjs に置換。

追加した検証

  • scripts/preflight-publish.mjsnpm run preflight)— publish 直前に実 tarball を検査。version / license / 内部依存の固定 / dist/ の実在 / build:packages への登録漏れ。
  • scripts/smoke-providers.mjsnpm run smoke)— 実 Chrome で全プロバイダを描画確認。プロバイダ間でスクリーンショットが全部異なることまで検査する。

検証結果

新規クローン + submodule 再帰で install → build → preflight まで通ることを確認。20 パッケージすべて 0.2.0 で clean。

examples/basic を実 Chrome で確認(map ページ、16 プロバイダ、全レンダリングが相異):12/16 OK。残り 4 件はいずれも依存更新とは無関係で、デプロイ済み main でも同じ失敗を再現:

状態原因
mapkit失敗.env の JWT が 2026-08-14 に期限切れ。かつ demo-react.mapconductor.com にオリジン固定
mappls失敗SDK URL が 401。キー側の問題
azuremaps失敗タイルが CORS で遮断。本番と同一出力(md5 一致)で以前から壊れている
google-maps-3d正常描画できている。テスト側のセレクタが <gmp-map-3d> の shadow DOM を拾えていなかっただけ

geojson-basic / geojson-layer / kml-layer / heatmap-layer × 5 プロバイダ = 20/20 OK(リネームした react-geojson の実描画確認を含む)。

RN パッケージは今回も見送り(すべて private: true のため publish 対象外)。

Step 8。android-for-template / ios-for-template と同じ構造。
TemplateMap(地図 SDK の代役)を置き換えるだけで、ホルダー・コントローラ・
レンダラの形はそのまま使える。
README.md が authoring guide を兼ねる(実装点 A〜H の一覧、コアが持っていて
書かなくてよいものの一覧、実際に作り込んだ不具合 5 件、ブラウザで確かめること)。
ルートの workspaces に登録したので CI のビルド・テストに乗る
(雛形が腐らない唯一の仕組み)。適合テスト 6 本が緑。
.gitignore の *.mjs に巻き込まれていた。外部の作者がコピーする出発点なので
追跡する必要がある。
 TemplateViewController.ts -> TemplateMapViewController.ts
TemplateMapView.tsx を追加(State サブクラス + React の入口 = 実装点 G)
地図デザイン型を MapDesignTypeInterface 準拠にした(getValue が要る)
VisibleRegion の組み立てをコアの buildVisibleRegion に寄せた
これで 3 プラットフォームの雛形が同じファイル構成になる:
TemplateMap / TemplateOverlays / TemplateMapViewController / TemplateMapView
+ README.md + 適合テスト
3 プラットフォームを並べて見比べるページなのに、カメラもマーカーも揃って
いなかった。同じ定数へ統一する。
## 揃えた値
- カメラ: エッフェル塔 (48.858140690309604, 2.2945027576710344) / ズーム 17 / bearing 270
- マーカー: 中心 1 個 + 同心円 5 本(各 8 個、45 度おき)= 41 個、間隔 60m
- 色: 中心 #111827、リングは内側から #E74C3C #E67E22 #F1C40F #2ECC71 #3498DB
- タップでバウンド
- 各マーカーの真下に半径 2.5m の円(塗り #FF00FF / 枠 黒 1px / clickable = false)
## それぞれの変更点
- android: すでにこの形。iOS / react と揃えた旨をコメントへ明記しただけ
- ios: マーカーと円が無かったので追加(カメラは元から一致)
- react: ホノルル / ズーム 11 / 3km 間隔だったのをエッフェル塔の値へ変更し、円を追加
## bearing 270 は意図的
北向きでないときにしか出ない不具合がある。実際
android-for-openmobilemaps で「アンカーのオフセットが画面空間ではなく
地図空間で適用される」不具合が、bearing 270 のこのページでだけ表面化した
(StoreMap は bearing 0 なので完璧に見えていた)。
## 円は常設の検証装置
円は地面に貼り付き、マーカーは画面固定サイズで描かれる。傾けたり回したりして
ピンの先端が円から外れたら、おかしいのはマーカー側だと一目で分かる。
## 確認
- android: assembleDebug / ktlintCheck 緑。実機(Lenovo TB520FU)の MapLibre で
41 個すべてのピンの先端が円の真上
- ios: xcodebuild BUILD SUCCEEDED
- react: tsc に Tilt ページ由来のエラー無し、vite build 成功
国土数値情報の鉄道データは属性が `N02_001` のような番号なので、吹き出しに
生のキーが並んでも何の値か分からなかった(値の方も "11" や "2" のコードなので、
見出しが読めないと手掛かりがゼロになる)。
N02_001 → 鉄道区分(railway category)
N02_002 → 事業者区分(business category)
N02_003 → 路線名(railway name)
N02_004 → 運営会社(railway company)
android / iOS / react(web)/ react-native の 4 つとも**同じ文言**にした。
3 プラットフォームを並べて見比べるサンプルなので、見出しが違うと
同じ地物を選んでいるのかどうかが判断できない。
web は既に日本語の見出しを持っていたが `N02_002` だけ「事業者種別」で、
android / iOS は生キーのままだった。
見出しが長くなったぶん、属性の列と値の列を半々にした。iOS の表の幅は
320pt のままにしてある(広げると iPhone の横幅で吹き出しが画面外へ出る)。
表に無いキーは今まで通り生のキー名を出すので、データ側に属性が増えても
表から消えることはない。
## 確認
- android: 実機(Lenovo TB520FU)で総武線をタップ → 4 行とも新しい見出しで表示
- iOS: シミュレータで半蔵門線をタップ → 同上。ktlint 相当(ビルド)緑
- react(web): Playwright で新宿線をタップ → 同上
- react-native: 同じ差し替えのみ。実機では未確認
なお XCUITest では吹き出しの中身を検証できない。InfoBubble は地図の
ビュー階層の中に描かれるため `staticTexts` に出てこない(画面には出ている)。
UI テストは足さず、目視で確認した。
吹き出しの見出しを「路線名(railway name)」のように併記していたが、日本語と英語が
1 行に混ざるうえ、値の方は「総武線」のままで英語にならなかった。
アプリの言語(web は言語セレクタ、それ以外は端末の言語)が日本語なら日本語、
それ以外なら英語で出すように変えた。
## 対訳をアプリではなくデータへ
`N02-22_GML.zip` の各フィーチャーに `N02_003_en`(路線名)と `N02_004_en`
(運営会社)を足した。549 路線・177 事業者ぶん。
アプリ側に対訳表を置くと、**同じ表を 4 つのサンプルへ配ることになり、
しかも必ずどれかが古くなる**。データが持っていれば、アプリがするのは
「日本語なら `N02_003`、英語なら `N02_003_en`」だけで済む。
英語名は、事業者が公式の英語名を出しているものはそれを使い(JR East /
Tokyo Metro / Kintetsu Railway)、それ以外はヘボン式で長音記号なし
(Sobu Line / Tokaido Shinkansen)。JR や私鉄の英語案内がその表記のため。
経緯は zip 内の README.md に書いた。
`N02_001`(鉄道区分)と `N02_002`(事業者区分)は元データがコード値なので、
見出しだけ訳して値はそのまま。
## 表示
- 見出し: 鉄道区分 / 事業者区分 / 路線名 / 運営会社 ⇔
Railway category / Business category / Railway name / Railway company
- ヘッダ行: プロパティ・値 ⇔ Property・Value
- `_en` で終わる属性は行として出さない(同じ項目が 2 行に増えるため)
- 表に無いキーは今まで通り生のキー名。データ側に属性が増えても消えない
zip は元より小さくなっている(2,428KB → 2,383KB)。属性が 2 つ増えたが、
geojson を字下げなしで書き出したぶんが上回った。
## 確認
日本語・英語の両方で、4 プラットフォームとも切り替わることを目視。
- android: 実機。端末が ja-JP なので `cmd locale set-app-locales` で英語も確認
(総武線/東日本旅客鉄道 ⇔ Sobu Line/JR East)
- iOS: シミュレータを `-AppleLanguages` で切り替え
(11号線半蔵門線/東京地下鉄 ⇔ Line 11 Hanzomon Line/Tokyo Metro)
- react(web): `/maplibre/geojson-layer` と `/ja` を Playwright で
(12号線大江戸線/東京都 ⇔ Line 12 Oedo Line/Tokyo Metropolitan Government)
- react-native: 端末の言語は `NativeModules` から取る(`react-native-localize` を
足さずに済ませるため)。型検査のみ。実機では未確認
なお iOS の吹き出しに accessibility を付けるのは見送った。吹き出しは地図ビューの
中に足したビューで、地図ビュー自身が accessibility のコンテナとして振る舞い
並べる要素を自前で決めているため、中身が読み上げにも UI テストにも出てこない。
`accessibilityElements` へ足しても効かなかった(MapLibre は container のメソッドを
上書きしている)。器を地図ビューの外へ出せば直るが、9 プロバイダ共通の
コア変更になるので、この変更には含めない。
submodule 側のコミット:
android-sdk-compose 55b6749 / ios-sdk-core 8b3c61f / js-sdk-react bdabdd4
android 実機で変更前後を切り出して比較し、帯の下の隙間が無くなったことを目視。
ktlintCheck / apiCheck 緑。iOS はコアとサンプルアプリのビルド成功。
react は型検査に影響なし。
ほとんどのプロバイダは 60 度で頭打ちになるが、**ArcGIS だけは 90 度近くまで実際に傾く**。
android-sdk が最初からその範囲を触れるよう ±89 にしてあるのに、react(web / react-native)
だけ ±60 のままだった。3 プラットフォームで同じページを並べて比べる前提なので揃える。
値は `TILT_LIMIT` として名前を付け、なぜ 89 なのかをその場に書いた。
JSX の属性の間にはコメントを置けないため、定数の宣言側に置いてある。
- workspaces / build:packages / dev:packages / api-surface に react-for-mappls
- examples/basic: ルーティング ('mappls')・ヘッダ・SingletonMaps・
MapViewContainer・デザインページ・camera-sync・PostOffice・Heatmap を配線
(tomtom/longdo と同型の機械的追加)。キーは VITE_MAPPLS_API_KEY (.env に設定済み)
- react-for-mappls 本体は同ディレクトリの独立リポジトリ (submodule 登録は
リモート作成後に .gitmodules へ追加すること)
react-for-mappls も同修正済み (独立リポジトリのため pointer なし)
- Fit Bounds: プリセット方式をやめ、android / ios と同じ
「マーカーをドラッグして矩形を指定 → ドロップで fitBounds、1.5 秒後に
矩形消去」に変更 (ドラッグ中は赤い矩形ポリゴンを表示)
- Tilt: android / ios にある TiltCameraDiagram (tilt に追従して動くカメラ図)
を SVG で移植 (同じ式・同じ配色)
- camera-restriction ページを新設: ios の CameraRestrictionPage と同一仕様
(制限トグル + Move outside / Zoom>max / Zoom<min / Reset + 読み出し)。
MapViewContainer に cameraRestriction プロップを追加 (restrictBounds より優先)。
maplibre で範囲外要求 → 35.73,139.80 へのクランプを確認
縦画面のスマホでは ControlPanel が地図を覆いすぎるため、
TiltCameraDiagram を max-width 760px かつ portrait のときだけ
display:none にする。横画面・デスクトップでは従来どおり表示。
Drop / Bounce のラベルを持つ 2 マーカー構成に変更し、タップで各マーカーに
紐づくアニメーションを実行する android / ios と同一仕様にする。
ボタン操作と D/B 表示の 1 マーカー構成は廃止。
…leton
GoogleProvider が mapViewState は google-3d、content スロットは google-2d 固定で
ずれていたため、google-maps-3d のクラスタページは隠れた 3D マップだけがズームし
見えている地図が反応しなかった。スロット id を state と同じ地図に合わせる。
…liders
info-bubble-styled を作り直し: マーカー 1 個 + 常時表示バブルに、
8 色スウォッチ 4 行(塗り / 枠線 / 文字 / マーカー)とスライダー 2 本
(枠線幅・マーカースケール 0.5〜2.0、0.25 刻み)。InfoBubble はスタイルを
登録時に焼き込むため key 再マウントで反映。js-sdk-react bump (borderWidth)。
@arcgis/core 5.1.17, mapbox-gl 3.28.1, maplibre-gl 6.3.0, ol 10.10.0,
cesium 1.144.0, azure-maps-control 3.7.4, @tomtom-org/maps-sdk 0.51.2.
All workspace packages build clean. react-for-arcgis submodule moves to
the @arcgis/core 5.x upgrade commit; react-for-mapbox picks up a typing
fix for mapbox-gl 3.28.
- react-geojson-layer を雛形にしたタイル描画パイプライン + KML パーサ/ローダ。
依存なしの反復 XML トークナイザ(再帰なし、20,000 段ネストをテストで実証)、
ZIP 署名判別による KMZ 読み込み(stored/deflate、DecompressionStream 使用)、
NetworkLink のキュー+訪問済みセット追跡(循環検出・maxDocuments・失敗スキップ)
- examples に kml-layer ページ(sample.kml 解析、クリックで属性テーブル)と
ドキュメント(en/ja/es-419)を追加。Playwright テスト 16 本
- React Native 対応は未実装(README に明記)
android-kml と同じ修正: 内部(穴を除く)判定を常に先に行い、外れた場合のみ
輪郭近傍にフォールバック。null 許容差を Android と同じ「未指定」扱いに統一。
ヒットテストの Playwright テストを追加し、ライブページのグリッドクリックで
穴だけ非ヒットになることを確認
- RN の Android ネイティブは mavenLocal の com.mapconductor:*:1.2.0 に固定されて
おり、8/9 の publish 以降に入った driver 層リファクタリング(Step 3〜7b)に
未追従だった。android-sdk を publishToMavenLocal し直し、RN 側ツールチェーンを
追従: Kotlin 2.3.20(Expo の pika が対応する最新。+1 マイナー前方互換で
Kotlin 2.4 メタデータを読める)、compileSdk 37、minSdk 28
- MapLibre 13.x AAR の prefab(共有ライブラリ + 静的 STL 宣言)が RN の CMake
検査 CXX1211 で落ちるため、:app の compileClasspath からのみ除外
(runtime には残るため実行時は正常)
- 各 submodule の修正を取り込み: ArcGIS/HERE デザイン id、HERE クラスタリング
レジストリ登録、./state サブパス、super.destroy()
Lenovo 実機で検証: 4 プロバイダの Store Map / ArcGIS Dark Gray / HERE
Logistics Night / HERE Post Office Cluster(24,526 件、初回表示から描画)
- js-sdk-react が RN 版で useMapUISettings を公開していなかったため、4 プロバイダ
とも state.uiSettings が完全な no-op だった。JS→ネイティブのコマンドを新設
- examples/reactnative-basic に UI Settings ページを追加(web と同じ 4 ジェスチャ)
- iOS の Pods はリファクタ前のファイル一覧で固まっていたため pod install で再生成
(MapViewState.swift が map/ 配下へ移動したのを追えていなかった)
…apability
RN のホルダーは MapViewHolder を直接 implements して 3 メソッドを手書きして
いたため、コアの既定(async は sync のラップ)から外れていた。あわせて
「同期投影を持たない」ことを capability として宣言し、無言の null 返しを
診断可能にする
MapLibre の既定 DemoTiles はこの地域のタイルを持たず地図が空になり、
ジェスチャ設定の効果が目視できなかった(他の RN サンプルと同じ扱いに揃える)
…implementation
コントローラ 675 行 × 4 + ホルダー 4 本 → 共有 698 行 + 各 9 行の派生。
longdo / tomtom を足すときに写しが増えない構造にする。
Lenovo 実機と iOS シミュレータで無回帰を確認(4 プロバイダの描画、
Polygon Click の Inside/Outside、マーカー 24,526 件、クラスタリング)
RN プロバイダ 1 本あたり src が約 180 行(ビュー 23 + コントローラ 9 +
ネイティブ定義 25 + 型/props)になり、地図 SDK 固有でない部分は
すべて js-sdk-react 側に集約された。longdo / tomtom を足すときに
写経する対象が無くなる。
Lenovo 実機と iOS シミュレータで無回帰を確認(4 プロバイダの描画、
クラスタリング 24,526 件、Polygon Click)
4 プロバイダに写経されていた 924 行(ペイロード変換とオーバーレイのデコード)を
js-sdk-react の共有 Kotlin へ集約。Lenovo 実機で Circle / Polygon / Polyline の
デコードが従来どおり動くことを確認
コマンド名/イベント名の写経をやめ、ラッパー interface で実装漏れを
コンパイルエラーにする。Lenovo 実機で animateCamera / fitBounds を含む
コマンド経路が従来どおり動くことを確認
iOS の RN ビューは 4 本合わせて 1,760 行 → 480 行。共通部分は js-sdk-react/ios の
MCReactNativeMapViewBase にあり、longdo/tomtom を足すときにコピーする必要はない。
reactnative-for-*/android の *MapViewWrapper.kt 4 本を、js-sdk-react の
MapConductorMapViewWrapperBase 継承 + プロバイダ固有ホストの形へ。
ios(MCReactNativeMapViewBase)と同じ構造。
写経で入り込んでいた挙動差も 1 本化した:
- カメラリスナーが最新カメラを保持(googlemaps / arcgis は代入漏れで常に null)
- updateMarker は未登録 id を composition で流し直す(コアの update は
未登録 id を黙って捨てるため maplibre / here では何も起きなかった)
- 呼ばれていなかった Wms84Projection.kt(179 行 ×2)と
restoreRequestedNegativeTiltCamera を削除
実機(TB520FU)で polygon-click の Inside / Outside、クラスタリング 24,526 件と
クラスタタップのズームイン、ヒートマップ、マーカーアニメーションを 4 プロバイダで確認。
ios-sdk の SwiftUI サンプル(MapConductorSampleApp)は 4 方向対応なのに、
RN サンプルだけ portrait 固定だった。地図は縦横比が変わるとプロバイダごとの
リサイズ経路を通るため、回転を塞いでいるとそこが一度も検証されない
(実際 ArcGIS Android の回転バグは長く気づかれていなかった)。
expo prebuild が生成する値と一致させるため 3 か所を揃える:
- app.config.ts: orientation を 'default' へ(設定の元。ここを直さないと
次の prebuild で portrait に戻る)
- ios/Info.plist: 4 方向(@expo/config-plugins の 'default' と同じ並び)
- android/AndroidManifest.xml: screenOrientation を unspecified へ
(同プラグインの 'default' と同じ値)
実機(TB520FU)で縦横どちらも表示・操作を確認。iOS はシミュレータと iPad の
両方でビルド・起動を確認し、アプリバンドルの Info.plist に 4 方向が入ることを確認。
新規プロバイダを足す前に、写経になっていた ViewManager を基盤側へ寄せた。
1 プロバイダあたり 19〜35 行になる。
シミュレータで GoogleMaps / ArcGIS の地図描画・マーカー表示を確認。
(コマンドを基底クラスに置くとマーカーが出なくなることを実機で確認し、
宣言だけマクロで各クラスに展開する形にしてある)
新規 RN プロバイダを足すコストを実証するための、動く最小のブリッジ。
android-for-template / ios-for-template / react-for-template と同じ構造にしてある。
react-for-template が「web のドライバー」の雛形なのに対し、こちらは
「既にあるネイティブドライバーを React Native へ出す」ための雛形。
実測したコスト(1 プロバイダあたり):
Android アダプタ 95 行
iOS アダプタ 90 行
JS 121 行
ObjC 25 行
Android ViewManager / Package 95 行
足回り(package.json / podspec / gradle 等)
本体は Android と iOS のアダプタ 2 つだけで、どちらも
「ネイティブの地図を作る / デザイン ID を訳す / 投影 / 破棄」の 4 点しかない。
残りは js-sdk-react の共通層が持つ。
サンプルアプリにも provider として登録し、実機(TB520FU)で
マーカー 25 件が正しい位置に描画されることを確認した。
雛形の地図は本物の SDK ではないため、Android は Compose Canvas を ComposeView で
包み、iOS は描画面を持たず空の UIView を返す(構造とコンパイルの検証が目的)。
README にその旨と、つまずきやすい 4 点を書いてある。
RN 化の 1 本目。変則的なものから進める方針で Longdo を選んだ。
WebView(Longdo Map API3)ベースで、他プロバイダと違う点が 3 つある。
1. 同期投影を持たない
ホルダーの toScreenOffset は android / iOS とも null を返す(SDK の性質)。
RN 側は js-sdk-react の Web Mercator 投影を使う
(screenProjection="webMercator")。黙って null を返しているのではない、と
分かるようコメントを残した。
2. android のホストは内側に Compose を持つ
Longdo はマーカーと InfoBubble を地図SDKではなく Compose のオーバーレイで
描くため、その層ごと LongdoMapSurface を載せる。ArcGIS の iOS が SwiftUI を
内側に持つのと同じ扱い。
3. マーカーの経路が 2 つある(少数=オーバーレイ / 多数=ラスタータイル)
どちらに倒すかはコントローラが件数を見て決める。RN 側に独自のゲートを
作らないこと。作ると「同一動作」が崩れる。
API キーは git 管理下のファイルに入れない。android は manifest の
${LONGDO_API_KEY} を gradle の manifestPlaceholders が、iOS は Info.plist の
$(LONGDO_API_KEY) を xcodebuild のビルド設定が埋める。どちらも .env.local が出所。
検証(両方のマーカー経路を見ること):
android 実機 TB520FU Store Map(25) / Post Offices(24,526) OK
iOS シミュレータ Store Map(25) / Post Offices(24,526) OK
iOS の InfoBubble はマーカー先端に正しく付く(JS 投影が効いている)
雛形の iOS が TemplateDesign を参照していてコンパイルできなかったのも直した
(正しくは TemplateMapDesignType / TemplateMapDesign.standard)。
サンプルの Podfile に MapConductorForTemplate を足して、雛形が腐らないようにした。
InfoBubble とマーカー追従の画面座標を JS 側の Web Mercator 投影で出していたが、
同じ式がネイティブにもあり 2 か所に増えていた。さらに JS 側の投影は
**タップの当たり判定(ネイティブ側にしかない)と食い違う**。
android / iOS とも、ホスト(LongdoMapHost / LongdoMapViewHolder)が
コアの WebMercatorScreenProjection で変換した値を返すようにして、
screenProjection="webMercator" の指定をやめた。
検証(どちらも JS 投影なし)
android 実機 HA26Z6JL
Store Map(25) マーカー描画・タップ・吹き出しの位置 OK
Post Offices(24,526・タイル) 描画・タップ・吹き出し OK
iOS シミュレータ iPhone 17 Pro
Store Map(25) OK
Post Offices(24,526・タイル) 地図を動かさずにタップして OK
(これが直前まで出なかった本体。ios-for-longdo 側の修正)
実機(iOS 17 以降)は外部ツールからスクリーンショットもタップも取れない。
screenshotr は無くなり、idb-companion は iOS 26 の実機を駆動できず、
devicectl にスクリーンショットは無い。**XCUITest しか道が無い。**
シミュレータだけでは足りない。iPhone 17 Pro は 3x、iPad は 2x で、
px と dp の取り違えは倍率の違う端末でこそ出る(android-for-longdo で実際に踏んだ)。
実際、これを入れて初めて iPad だけで落ちる経路が見つかった(下記)。
構成
ios-uitests/ Swift 本体(ios/ の外。prebuild で消えないように)
scripts/add-ui-test-target.rb
ターゲットとスキームへの配線。何度流しても同じ結果になる
(CocoaPods 同梱の xcodeproj を使うので追加の gem は不要)
サンプル側に accessibilityLabel を足した。RN のこのアプリは `Text` が要素ツリーに
名前の無い Other としてしか出ないため、ラベルの無い行は座標でしか触れない。
プロバイダ名は `PROVIDER_LABELS` に集約して、ボタンの文字・一覧の文字・
ラベルの出所を 1 つにした(3 か所に散らすと片方だけ直してテストが見失う)。
実機(iPad Pro 11-inch / iOS 26.5.2)で分かったこと
- Post Office(24,526 件・タイル経路): タップで InfoBubble が出る。回転しても残る
- Store Map(25 件・オーバーレイ経路): **同じ操作で出ない**。
同じテストがシミュレータでは通るので、実機固有。原因は未特定(todo に記録)
- マーカーを連打で探すと WebView が地図を動かす(9 → 8 までズームアウトした)。
1 タップで判定すること
iPad の iPhone 互換ウィンドウ(実測 375x667 論理 / 613x1092 画面)では
`app.coordinate(withNormalizedOffset:)` が壊れる。論理サイズで正規化した点を
**画面座標として**配送するため二重変換になり、中心 (0.5,0.5) を叩いたつもりが
論理 (46.8,172.6) に落ちていた。実機のログで確認した:
要求 (0.5,0.5) → nativeTap local=(46.8, 88.6)
geo=21.6256,-158.1263 ← オアフ島北西の海上
markerHit=false
window 基準に変えると狙いどおりになる:
要求 (0.5,0.5) → nativeTap local=(187.5, 249.5) ← 地図の中心(375/2)
geo=21.4200,-157.9331 ← ホノルル
markerHit=true
要素のタップが効いていたのは、要素の frame も同じ空間で報告されて誤差が
相殺されるため。ios-sdk の既存 UI テスト(LongdoInfoBubbleUITests)も
最初から window 基準だった。**そちらに合わせる。**
これで実機の通しテストが通る(82.8 秒):
Post Office(24,526 件・タイル経路)タップ → InfoBubble / 回転後も残る
Store Map(25 件・オーバーレイ経路)タップ → InfoBubble
**先の「iPad 実機だけ Store Map のタップが効かない」は誤りだった。**
製品の不具合ではなく、このテストの座標指定の誤り。
**このテストはいま落ちる。** 実機で見つかった不具合を固定するために入れる。
症状: Longdo でマーカーをタップすると吹き出しは出るが、地図をパンしても
吹き出しがその場に residual として残り、尻尾が無関係な場所を指す。
実機(iPad Pro 11-inch)で採った証跡:
emitCamera 呼び出し … 2 回(起動時のみ。パン中は 0 回)
吹き出しの画面座標 … 33 回送信して全部同じ値
bubbleFrame before == after(1pt も動かない)
スクリーンショットでは地図だけが動いている
原因の在りか: iOS の `LongdoMapHost.bindEvents()` は Longdo の高レベルイベント
(Location / Zoom / Rotate / Pitch)しか購読していない。これらは**ドラッグ中に
発火しない**ため、投影キャッシュも無効化されず、位置が計算し直されない。
android-for-longdo は内部レンダラの move も購読している:
map.Renderer.on('move', window.__mcProject) (LongdoMapView.kt:640)
テストを書くときの注意(実機で 2 回空振りした)
- 吹き出しの上を通るドラッグは効かない。吹き出しは RN のビューでジェスチャを吸う
- 速いドラッグは WebView に取られない。ゆっくり引いて離す前に止める
reactnative-for-template から起こして TomTom 用に置換したもの。
JS 層は型チェックとビルドが通る。**まだ実機で動かしていない**
(サンプルアプリへの登録と両プラットフォームのビルドが残っている)。
TomTom 固有として書いたのは 2 点。
1. Android のライフサイクル配線(`attachLifecycle`)
TomTom の MapView は `onStart` / `onResume` を受け取らないとタイルを
1 枚も描かない(Compose 版は DisposableEffect で送っており、その旨の
コメントが android-for-tomtom にある)。RN には対応する仕組みが無く、
他の RN プロバイダは onDestroy しか呼んでいない。生成時に即送り、
以降の前面/背面は ReactContext の LifecycleEventListener で追う。
2. `setupMarkerTileRaster` の呼び出し
忘れると大量マーカーが黙って 1 つも出ない。Compose 版と同じ関数を通す。
iOS は ios-for-tomtom から切り出した `TomTomMapHost` を呼ぶだけ。
投影はホルダー(`map.pointForCoordinate`)に聞く — Longdo と違い TomTom は
同期投影を持つので、ホスト側の自前計算へ迂回しない。
state は `@mapconductor/react-for-tomtom/state` から取る(ルートは
`@tomtom-org/maps-sdk` を実行時に引き込み Metro/Hermes が落ちる)。
全パッケージを 0.2.0 に上げ、内部依存レンジを ^0.2.0 に揃える。
license を "Apache2"(不正な SPDX)から "Apache-2.0" に修正。
react-geojson-layer -> react-geojson にリネーム。
npm パッケージ名 / ディレクトリ / submodule / api ベースライン /
examples / docs / CLAUDE.md をすべて追従。
GeoJSONLayer などの公開 API 名は変更しない。
地図 SDK 依存を宣言レンジ内の最新へ更新(lockfile)。
実際に動いたのは maplibre-gl 6.3.0 -> 6.4.0 と @turf/turf 7.3.5 -> 7.4.0 の 2 つだけで、
他は既にレンジ上の最新だった。react-for-tomtom の maplibre-gl だけ 5.x 据え置き
(@tomtom-org/maps-sdk が ^5.24.0 依存。理由は CLAUDE.md に記載)。
リリース経路の穴を 2 つ塞ぐ:
- react-for-mappls / react-icons-{jp,us,weather} が親リポジトリに 1 ファイルも
追跡されていなかった。react-for-mappls は公開対象なのに CI のチェックアウトに
存在せず、ビルドも publish もされない状態だった。submodule として登録。
- npm-publish の manifest 復元が `git submodule foreach` だったため、submodule で
ない react-for-mappls / react-kml / reactnative-for-* の "*" 依存が復元されず、
そのまま publish される経路があった。scripts/unlink-local-workspaces.mjs に置換し、
"*" が残っていたら fail するチェックを追加。
scripts/smoke-providers.mjs を追加(npm run smoke)。
実 Chrome で全プロバイダを描画確認する。プロバイダ間でスクリーンショットが
全部異なることまで検査する。hello-map は MapLibre 固定なので、そこを叩くと
16/16 OK になるが何も検証していない(実際に踏んだ)。
deploy-basic-example に VITE_MAPPLS_API_KEY を追加(未配線だった)。
.gitignore は *.mjs を無視し、追跡したい 1 本ずつに negation を足す運用だった。
そのため今回足した scripts/unlink-local-workspaces.mjs と
scripts/smoke-providers.mjs が `git add -A` で無言のまま無視され、
親リポジトリのコミットにも CI のチェックアウトにも入っていなかった。
npm-publish はこの unlink スクリプトを呼ぶので、そのまま流すと
「スクリプトが無い -> manifest が "*" のまま -> 全パッケージが
@mapconductor/* を "*" 依存で publish される」経路になっていた。
新規クローンで実際に再現し、同時に足した wildcard チェックが検出した。
scripts/ はソースしか置かないので !scripts/*.mjs で丸ごと追跡する。
@mapconductor/react-for-mapkit@0.1.3 は npm 上で LICENSE / README /
package.json しか含んでいない。main が指す ./dist/index.js が存在しないので
install しても import できない。公開パッケージの中で唯一 build:packages に
入っておらず、ビルドされないまま files: ["dist"] が何にもマッチせず、
npm はそれを警告なしに公開していた。
build:packages と dev:packages に react-for-mapkit を追加。0.2.0 で解消する。
scripts/preflight-publish.mjs を追加(npm run preflight)。
publish 直前に実際の tarball を検査する:
- version / license(SPDX) / files フィールド
- @mapconductor/* の依存が ^<version> に固定されているか
- tarball に dist/ が実在するか、node_modules が混入していないか
- 公開対象が build:packages に登録されているか
最初の 1 件で止めず全件出す。publish 後に 2 件目が判明するのが一番高くつくため。
npm-publish ワークフローの publish 手前に組み込む。
wildcard チェックの grep 除外パターンが `^\./\?examples/` で先頭のドットを
必須にしていて examples/ を除外できていなかったのを修正。
@wf9a5m75
wf9a5m75 merged commit fd618fe into mainAug 16, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@wf9a5m75