[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E6FMTAY9F0B1KNVRPC6VT7":3,"skill-same:01M6E6FMTAY9F0B1KNVRPC6VT7":78},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":31,"linkOnly":32,"markdownContent":33,"markdownBody":34,"frontMatter":35,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":42,"repositoryHtmlUrl":43,"repositoryHomepageUrl":44,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":46,"updatedAt":47,"install":48,"resources":61},"01M6E6FMTAY9F0B1KNVRPC6VT7","axiom-mapkit",null,"Use when implementing maps, annotations, search, directions, or debugging MapKit display\u002Fperformance issues - SwiftUI Map, MKMapView, MKLocalSearch, clustering, Look Around","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-mapkit\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30],"axiom mapkit","performance","debugging","issues","search","clustering","swiftui","annotations","use","look","maps","map","2026-11-10 10:12:30",false,"---\nname: axiom-mapkit\ndescription: Use when implementing maps, annotations, search, directions, or debugging MapKit display\u002Fperformance issues - SwiftUI Map, MKMapView, MKLocalSearch, clustering, Look Around\nlicense: MIT\nmetadata:\n  version: \"1.0.0\"\n  last-updated: \"2026-02-26\"\n---\n\n# MapKit Patterns\n\nMapKit patterns and anti-patterns for iOS apps. Prevents common mistakes: using MKMapView when SwiftUI Map suffices, annotations in view bodies, setRegion loops, and performance issues with large annotation counts.\n\n## When to Use\n\n- Adding a map to your iOS app\n- Displaying annotations, markers, or custom pins\n- Implementing search (address, POI, autocomplete)\n- Adding directions\u002Frouting to a map\n- Debugging map display issues (annotations not showing, region jumping)\n- Optimizing map performance with many annotations\n- Deciding between SwiftUI Map and MKMapView\n\n## Related Skills\n\n- `axiom-mapkit-ref` — Complete API reference\n- `axiom-mapkit-diag` — Symptom-based troubleshooting\n- `axiom-core-location` — Location authorization and monitoring\n\n---\n\n## Part 1: Anti-Patterns (with Time Costs)\n\n| Anti-Pattern | Time Cost | Fix |\n|---|---|---|\n| Using MKMapView when SwiftUI Map suffices | 2-4 hours UIViewRepresentable boilerplate | Use SwiftUI `Map {}` for standard map features (iOS 17+) |\n| Creating annotations in SwiftUI view body | UI freeze with 100+ items, view recreation on every update | Move annotations to model, use `@State` or `@Observable` |\n| No annotation view reuse (MKMapView) | Memory spikes, scroll lag with 500+ annotations | `dequeueReusableAnnotationView(withIdentifier:for:)` |\n| `setRegion` in `updateUIView` without guard | Infinite loop — region change triggers update, update sets region | Guard with `mapView.region != region` or use flag |\n| Ignoring MapCameraPosition (SwiftUI) | Can't programmatically control camera, broken \"center on user\" | Bind `position` parameter to `@State var cameraPosition` |\n| Synchronous geocoding on main thread | UI freeze for 1-3 seconds per geocode | Use `CLGeocoder().geocodeAddressString` with async\u002Fawait |\n| Not filtering annotations to visible region | Loading all 10K annotations at once | Use `mapView.annotations(in:)` or fetch by visible region |\n| Ignoring `resultTypes` in MKLocalSearch | Irrelevant results, slow search | Set `.resultTypes = [.pointOfInterest]` or `.address` to filter |\n\n---\n\n## Part 2: Decision Trees\n\n### Decision Tree 1: SwiftUI Map vs MKMapView\n\n```dot\ndigraph {\n    \"Need map in app?\" [shape=diamond];\n    \"iOS 17+ target?\" [shape=diamond];\n    \"Need custom tile overlay?\" [shape=diamond];\n    \"Need fine-grained delegate control?\" [shape=diamond];\n    \"Use SwiftUI Map\" [shape=box];\n    \"Use MKMapView\\nvia UIViewRepresentable\" [shape=box];\n\n    \"Need map in app?\" -> \"iOS 17+ target?\" [label=\"yes\"];\n    \"iOS 17+ target?\" -> \"Need custom tile overlay?\" [label=\"yes\"];\n    \"iOS 17+ target?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"no\"];\n    \"Need custom tile overlay?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"yes\"];\n    \"Need custom tile overlay?\" -> \"Need fine-grained delegate control?\" [label=\"no\"];\n    \"Need fine-grained delegate control?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"yes\"];\n    \"Need fine-grained delegate control?\" -> \"Use SwiftUI Map\" [label=\"no\"];\n}\n```\n\n#### When SwiftUI Map is Right (most apps)\n\n- Standard map with markers and annotations\n- Programmatic camera control\n- Built-in user location display\n- Shape overlays (circle, polygon, polyline)\n- Map style selection (standard, imagery, hybrid)\n- Selection handling\n- Clustering\n\n#### When MKMapView is Required\n\n- Custom tile overlays (e.g., OpenStreetMap, custom imagery)\n- Fine-grained delegate control (willBeginLoadingMap, didFinishLoadingMap)\n- Custom annotation view animations beyond SwiftUI\n- Pre-iOS 17 deployment target\n- Advanced overlay rendering with custom MKOverlayRenderer subclasses\n\n### Decision Tree 2: Annotation Strategy by Count\n\n```\nAnnotation count?\n├─ \u003C 100 → Use Marker\u002FAnnotation directly in Map {} content builder\n│   Simple, declarative, no performance concern\n│\n├─ 100-1000 → Enable clustering\n│   Set .clusteringIdentifier on annotation views\n│   SwiftUI: Marker(\"\", coordinate:).tag(id)\n│   MKMapView: view.clusteringIdentifier = \"poi\"\n│\n└─ 1000+ → Server-side clustering or visible-region filtering\n    Fetch only annotations within mapView.region\n    Or pre-cluster on server, send cluster centroids\n    MKMapView with view reuse is preferred for very large datasets\n```\n\n#### Visible-Region Filtering (SwiftUI)\n\nOnly load annotations within the visible map region. Prevents loading all 10K+ annotations at once:\n\n```swift\nstruct MapView: View {\n    @State private var cameraPosition: MapCameraPosition = .automatic\n    @State private var visibleAnnotations: [Location] = []\n\n    let allLocations: [Location]  \u002F\u002F Full dataset\n\n    var body: some View {\n        Map(position: $cameraPosition) {\n            ForEach(visibleAnnotations) { location in\n                Marker(location.name, coordinate: location.coordinate)\n            }\n        }\n        .onMapCameraChange(frequency: .onEnd) { context in\n            visibleAnnotations = allLocations.filter { location in\n                context.region.contains(location.coordinate)\n            }\n        }\n    }\n}\n\nextension MKCoordinateRegion {\n    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {\n        let latRange = (center.latitude - span.latitudeDelta \u002F 2)...(center.latitude + span.latitudeDelta \u002F 2)\n        let lngRange = (center.longitude - span.longitudeDelta \u002F 2)...(center.longitude + span.longitudeDelta \u002F 2)\n        return latRange.contains(coordinate.latitude) && lngRange.contains(coordinate.longitude)\n    }\n}\n```\n\n#### Why Clustering Matters\n\nWithout clustering at 500 annotations:\n- Map is unreadable (pins overlap completely)\n- Scroll\u002Fzoom lag increases with every annotation\n- Memory grows linearly with annotation count\n\nWith clustering:\n- User sees meaningful groups with counts\n- Only visible cluster markers rendered\n- Tap to expand reveals individual annotations\n\n### Decision Tree 3: Search and Directions\n\n```\nSearch implementation:\n├─ User types search query\n│   └─ MKLocalSearchCompleter (real-time autocomplete)\n│       Configure: resultTypes, region bias\n│       └─ User selects result\n│           └─ MKLocalSearch (full result with MKMapItem)\n│               Use completion.title for MKLocalSearch.Request\n│\n└─ Programmatic search (e.g., \"nearest gas station\")\n    └─ MKLocalSearch with naturalLanguageQuery\n        Configure: resultTypes, region, pointOfInterestFilter\n\nDirections implementation:\n├─ MKDirections.Request\n│   Set source (MKMapItem.forCurrentLocation()) and destination\n│   Set transportType (.automobile, .walking, .transit)\n│\n└─ MKDirections.calculate()\n    └─ MKRoute\n        ├─ .polyline → Display as MapPolyline or MKPolylineRenderer\n        ├─ .expectedTravelTime → Show ETA\n        ├─ .distance → Show distance\n        └─ .steps → Turn-by-turn instructions\n```\n\n---\n\n## Part 3: Pressure Scenarios\n\n### Scenario 1: \"Just Wrap MKMapView in UIViewRepresentable\"\n\n**Setup**: Adding a map to a SwiftUI app. Developer is familiar with MKMapView from UIKit projects.\n\n**Pressure**: \"I know MKMapView well. SwiftUI Map is new and might be limited.\"\n\n**Expected with skill**: Check the decision tree. If the app needs standard markers, annotations, camera control, user location, and shape overlays — SwiftUI Map handles all of that. Use it.\n\n**Anti-pattern without skill**: 200+ lines of UIViewRepresentable + Coordinator wrapping MKMapView, manually bridging state, implementing delegate methods for annotation views, fighting updateUIView infinite loops — when 20 lines of `Map {}` with content builder would have worked.\n\n**Time cost**: 2-4 hours of unnecessary boilerplate + ongoing maintenance burden.\n\n**The test**: Can you list a specific feature the app needs that SwiftUI Map cannot provide? If not, use SwiftUI Map.\n\n### Scenario 2: \"Add All 10,000 Pins to the Map\"\n\n**Setup**: App has a database of 10,000 location data points. Product manager wants users to see all locations on the map.\n\n**Pressure**: \"Users need to see ALL locations. Just add them all.\"\n\n**Expected with skill**: Use clustering + visible region filtering. 10K annotations without clustering is unusable — pins overlap, scrolling lags, memory spikes. Clustering shows meaningful groups. Visible region filtering loads only what's on screen.\n\n**Anti-pattern without skill**: Adding all 10,000 annotations at once. Map becomes an unreadable blob of overlapping pins. Scroll lag makes the app feel broken. Memory usage spikes 200-400MB.\n\n**Implementation path**:\n1. Enable clustering (`.clusteringIdentifier`)\n2. Fetch annotations only within visible region (`.onMapCameraChange` + query)\n3. Server-side pre-clustering for datasets > 5K if possible\n\n### Scenario 3: \"Search Isn't Finding Results\"\n\n**Setup**: MKLocalSearch returns irrelevant or empty results. Developer considers adding Google Maps SDK.\n\n**Pressure**: \"MapKit search is broken. Let me add a third-party SDK.\"\n\n**Expected with skill**: Check configuration first. MapKit search needs:\n1. `resultTypes` — filter to `.pointOfInterest` or `.address` (default returns everything)\n2. `region` — bias results to the visible map region\n3. Query format — natural language like \"coffee shops\" works; structured queries don't\n\n**Anti-pattern without skill**: Adding Google Maps SDK (50+ MB binary, API key management, billing setup) when MapKit search works correctly with proper configuration.\n\n**Time cost**: 4-8 hours adding third-party SDK vs 5 minutes configuring MapKit search.\n\n---\n\n## Part 4: Core Location Integration\n\nMapKit and Core Location interact in ways that surprise developers.\n\n### Implicit Authorization\n\nWhen you set `showsUserLocation = true` on MKMapView or add `UserAnnotation()` in SwiftUI Map, MapKit implicitly requests location authorization if it hasn't been requested yet.\n\nThis means:\n- The authorization prompt appears at map display time, not when the developer expects\n- The user sees a prompt with no context about why location is needed\n- If denied, the blue dot silently doesn't appear\n\n#### Recommended Pattern\n\nRequest authorization explicitly BEFORE showing the map:\n\n```swift\n\u002F\u002F 1. Request authorization with context\nlet session = CLServiceSession(authorization: .whenInUse)\n\n\u002F\u002F 2. Then show map with user location\nMap {\n    UserAnnotation()\n}\n```\n\n### CLServiceSession (iOS 17+)\n\nFor continuous location display on a map, create a `CLServiceSession`:\n\n```swift\n@Observable\nclass MapModel {\n    var cameraPosition: MapCameraPosition = .automatic\n    private var locationSession: CLServiceSession?\n\n    func startShowingUserLocation() {\n        locationSession = CLServiceSession(authorization: .whenInUse)\n    }\n\n    func stopShowingUserLocation() {\n        locationSession = nil\n    }\n}\n```\n\n### Cross-Reference\n\nFor full authorization decision trees, monitoring patterns, and background location:\n- `axiom-core-location` — Authorization strategy, monitoring approach\n- `axiom-core-location-diag` — \"Location not working\" troubleshooting\n- `axiom-energy` — Location as battery subsystem\n\n---\n\n## Part 5: SwiftUI Map Quick Start\n\nThe most common pattern — a map with markers and user location:\n\n```swift\nstruct ContentView: View {\n    @State private var cameraPosition: MapCameraPosition = .automatic\n    @State private var selectedItem: MKMapItem?\n\n    let locations: [Location]  \u002F\u002F Your model\n\n    var body: some View {\n        Map(position: $cameraPosition, selection: $selectedItem) {\n            UserAnnotation()\n\n            ForEach(locations) { location in\n                Marker(location.name, coordinate: location.coordinate)\n                    .tint(location.category.color)\n            }\n        }\n        .mapStyle(.standard(elevation: .realistic))\n        .mapControls {\n            MapUserLocationButton()\n            MapCompass()\n            MapScaleView()\n        }\n        .onChange(of: selectedItem) { _, item in\n            if let item {\n                handleSelection(item)\n            }\n        }\n    }\n}\n```\n\n#### Key Points\n\n- `@State var cameraPosition` — bind for programmatic camera control\n- `selection: $selectedItem` — handle tap on markers\n- `MapCameraPosition.automatic` — system manages initial view\n- `.mapControls {}` — built-in UI for location button, compass, scale\n- `ForEach` in content builder — dynamic annotations from data\n\n---\n\n## Part 6: Search Implementation Pattern\n\nComplete search with autocomplete:\n\n```swift\n@Observable\nclass SearchModel {\n    var searchText = \"\"\n    var completions: [MKLocalSearchCompletion] = []\n    var searchResults: [MKMapItem] = []\n\n    private let completer = MKLocalSearchCompleter()\n    private var completerDelegate: CompleterDelegate?\n\n    init() {\n        completerDelegate = CompleterDelegate { [weak self] results in\n            self?.completions = results\n        }\n        completer.delegate = completerDelegate\n        completer.resultTypes = [.pointOfInterest, .address]\n    }\n\n    func updateSearch(_ text: String) {\n        searchText = text\n        completer.queryFragment = text\n    }\n\n    func search(for completion: MKLocalSearchCompletion) async throws {\n        let request = MKLocalSearch.Request(completion: completion)\n        request.resultTypes = [.pointOfInterest, .address]\n        let search = MKLocalSearch(request: request)\n        let response = try await search.start()\n        searchResults = response.mapItems\n    }\n\n    func search(query: String, in region: MKCoordinateRegion) async throws {\n        let request = MKLocalSearch.Request()\n        request.naturalLanguageQuery = query\n        request.region = region\n        request.resultTypes = .pointOfInterest\n        let search = MKLocalSearch(request: request)\n        let response = try await search.start()\n        searchResults = response.mapItems\n    }\n}\n```\n\n#### MKLocalSearchCompleter Delegate (Required)\n\n```swift\nclass CompleterDelegate: NSObject, MKLocalSearchCompleterDelegate {\n    let onUpdate: ([MKLocalSearchCompletion]) -> Void\n\n    init(onUpdate: @escaping ([MKLocalSearchCompletion]) -> Void) {\n        self.onUpdate = onUpdate\n    }\n\n    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {\n        onUpdate(completer.results)\n    }\n\n    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {\n        \u002F\u002F Handle error — network issues, rate limiting\n    }\n}\n```\n\n#### Rate Limiting\n\nApple rate-limits MapKit search. For autocomplete:\n- `MKLocalSearchCompleter` handles its own throttling internally\n- Don't create a new completer per keystroke — reuse one instance\n- Set `queryFragment` on each keystroke; the completer debounces\n\nFor `MKLocalSearch`:\n- Don't fire a search on every keystroke — use the completer for autocomplete\n- Fire `MKLocalSearch` only when the user selects a completion or submits\n\n---\n\n## Part 7: Directions Implementation Pattern\n\n```swift\nfunc calculateDirections(\n    from source: CLLocationCoordinate2D,\n    to destination: MKMapItem,\n    transportType: MKDirectionsTransportType = .automobile\n) async throws -> MKRoute {\n    let request = MKDirections.Request()\n    request.source = MKMapItem(placemark: MKPlacemark(coordinate: source))\n    request.destination = destination\n    request.transportType = transportType\n\n    let directions = MKDirections(request: request)\n    let response = try await directions.calculate()\n\n    guard let route = response.routes.first else {\n        throw MapError.noRouteFound\n    }\n    return route\n}\n```\n\n#### Displaying the Route (SwiftUI)\n\n```swift\nMap(position: $cameraPosition) {\n    if let route {\n        MapPolyline(route.polyline)\n            .stroke(.blue, lineWidth: 5)\n    }\n\n    Marker(\"Start\", coordinate: startCoord)\n    Marker(\"End\", coordinate: endCoord)\n}\n```\n\n#### Displaying the Route (MKMapView)\n\n```swift\n\u002F\u002F Add overlay\nmapView.addOverlay(route.polyline, level: .aboveRoads)\n\n\u002F\u002F Implement renderer delegate\nfunc mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {\n    if let polyline = overlay as? MKPolyline {\n        let renderer = MKPolylineRenderer(polyline: polyline)\n        renderer.strokeColor = .systemBlue\n        renderer.lineWidth = 5\n        return renderer\n    }\n    return MKOverlayRenderer(overlay: overlay)\n}\n```\n\n#### Route Information\n\n```swift\nlet route: MKRoute = ...\nlet travelTime = route.expectedTravelTime  \u002F\u002F TimeInterval in seconds\nlet distance = route.distance              \u002F\u002F CLLocationDistance in meters\nlet steps = route.steps                    \u002F\u002F [MKRoute.Step]\n\nfor step in steps {\n    print(\"\\(step.instructions) — \\(step.distance)m\")\n    \u002F\u002F \"Turn right on Main St — 450m\"\n}\n```\n\n---\n\n## Part 8: Clustering Pattern\n\n### SwiftUI (iOS 17+)\n\n```swift\nMap(position: $cameraPosition) {\n    ForEach(locations) { location in\n        Marker(location.name, coordinate: location.coordinate)\n            .tag(location.id)\n    }\n    .mapItemClusteringIdentifier(\"locations\")\n}\n```\n\n### MKMapView\n\n```swift\nfunc mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {\n    if let cluster = annotation as? MKClusterAnnotation {\n        let view = mapView.dequeueReusableAnnotationView(\n            withIdentifier: \"cluster\",\n            for: annotation\n        ) as! MKMarkerAnnotationView\n        view.markerTintColor = .systemBlue\n        view.glyphText = \"\\(cluster.memberAnnotations.count)\"\n        return view\n    }\n\n    let view = mapView.dequeueReusableAnnotationView(\n        withIdentifier: \"pin\",\n        for: annotation\n    ) as! MKMarkerAnnotationView\n    view.clusteringIdentifier = \"locations\"\n    view.markerTintColor = .systemRed\n    return view\n}\n```\n\n#### Clustering Requirements\n\n1. All annotation views that should cluster MUST share the same `clusteringIdentifier`\n2. Register annotation view classes: `mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: \"pin\")`\n3. Clustering only activates when annotations physically overlap at the current zoom level\n4. System manages cluster\u002Funcluster animation automatically\n\n---\n\n## Part 9: Pre-Release Checklist\n\n- [ ] Map loads and displays correctly\n- [ ] Annotations appear at correct coordinates (lat\u002Flng not swapped)\n- [ ] Clustering works with 100+ annotations\n- [ ] Search returns relevant results (resultTypes configured)\n- [ ] Camera position controllable programmatically\n- [ ] Memory stable when scrolling\u002Fzooming with many annotations\n- [ ] User location shows correctly (authorization handled before display)\n- [ ] Directions render as polyline overlay\n- [ ] Map works in Dark Mode (map styles adapt automatically)\n- [ ] Accessibility: VoiceOver announces map elements\n- [ ] No setRegion\u002FupdateUIView infinite loops (if using MKMapView)\n- [ ] MKLocalSearchCompleter reused (not recreated per keystroke)\n- [ ] Annotation views reused via `dequeueReusableAnnotationView` (MKMapView)\n- [ ] Look Around availability checked before displaying (`MKLookAroundSceneRequest`)\n\n---\n\n## Resources\n\n**WWDC**: 2023-10043, 2024-10094\n\n**Docs**: \u002Fmapkit, \u002Fmapkit\u002Fmap\n\n**Skills**: mapkit-ref, mapkit-diag, core-location\n\n","# MapKit Patterns\n\nMapKit patterns and anti-patterns for iOS apps. Prevents common mistakes: using MKMapView when SwiftUI Map suffices, annotations in view bodies, setRegion loops, and performance issues with large annotation counts.\n\n## When to Use\n\n- Adding a map to your iOS app\n- Displaying annotations, markers, or custom pins\n- Implementing search (address, POI, autocomplete)\n- Adding directions\u002Frouting to a map\n- Debugging map display issues (annotations not showing, region jumping)\n- Optimizing map performance with many annotations\n- Deciding between SwiftUI Map and MKMapView\n\n## Related Skills\n\n- `axiom-mapkit-ref` — Complete API reference\n- `axiom-mapkit-diag` — Symptom-based troubleshooting\n- `axiom-core-location` — Location authorization and monitoring\n\n---\n\n## Part 1: Anti-Patterns (with Time Costs)\n\n| Anti-Pattern | Time Cost | Fix |\n|---|---|---|\n| Using MKMapView when SwiftUI Map suffices | 2-4 hours UIViewRepresentable boilerplate | Use SwiftUI `Map {}` for standard map features (iOS 17+) |\n| Creating annotations in SwiftUI view body | UI freeze with 100+ items, view recreation on every update | Move annotations to model, use `@State` or `@Observable` |\n| No annotation view reuse (MKMapView) | Memory spikes, scroll lag with 500+ annotations | `dequeueReusableAnnotationView(withIdentifier:for:)` |\n| `setRegion` in `updateUIView` without guard | Infinite loop — region change triggers update, update sets region | Guard with `mapView.region != region` or use flag |\n| Ignoring MapCameraPosition (SwiftUI) | Can't programmatically control camera, broken \"center on user\" | Bind `position` parameter to `@State var cameraPosition` |\n| Synchronous geocoding on main thread | UI freeze for 1-3 seconds per geocode | Use `CLGeocoder().geocodeAddressString` with async\u002Fawait |\n| Not filtering annotations to visible region | Loading all 10K annotations at once | Use `mapView.annotations(in:)` or fetch by visible region |\n| Ignoring `resultTypes` in MKLocalSearch | Irrelevant results, slow search | Set `.resultTypes = [.pointOfInterest]` or `.address` to filter |\n\n---\n\n## Part 2: Decision Trees\n\n### Decision Tree 1: SwiftUI Map vs MKMapView\n\n```dot\ndigraph {\n    \"Need map in app?\" [shape=diamond];\n    \"iOS 17+ target?\" [shape=diamond];\n    \"Need custom tile overlay?\" [shape=diamond];\n    \"Need fine-grained delegate control?\" [shape=diamond];\n    \"Use SwiftUI Map\" [shape=box];\n    \"Use MKMapView\\nvia UIViewRepresentable\" [shape=box];\n\n    \"Need map in app?\" -> \"iOS 17+ target?\" [label=\"yes\"];\n    \"iOS 17+ target?\" -> \"Need custom tile overlay?\" [label=\"yes\"];\n    \"iOS 17+ target?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"no\"];\n    \"Need custom tile overlay?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"yes\"];\n    \"Need custom tile overlay?\" -> \"Need fine-grained delegate control?\" [label=\"no\"];\n    \"Need fine-grained delegate control?\" -> \"Use MKMapView\\nvia UIViewRepresentable\" [label=\"yes\"];\n    \"Need fine-grained delegate control?\" -> \"Use SwiftUI Map\" [label=\"no\"];\n}\n```\n\n#### When SwiftUI Map is Right (most apps)\n\n- Standard map with markers and annotations\n- Programmatic camera control\n- Built-in user location display\n- Shape overlays (circle, polygon, polyline)\n- Map style selection (standard, imagery, hybrid)\n- Selection handling\n- Clustering\n\n#### When MKMapView is Required\n\n- Custom tile overlays (e.g., OpenStreetMap, custom imagery)\n- Fine-grained delegate control (willBeginLoadingMap, didFinishLoadingMap)\n- Custom annotation view animations beyond SwiftUI\n- Pre-iOS 17 deployment target\n- Advanced overlay rendering with custom MKOverlayRenderer subclasses\n\n### Decision Tree 2: Annotation Strategy by Count\n\n```\nAnnotation count?\n├─ \u003C 100 → Use Marker\u002FAnnotation directly in Map {} content builder\n│   Simple, declarative, no performance concern\n│\n├─ 100-1000 → Enable clustering\n│   Set .clusteringIdentifier on annotation views\n│   SwiftUI: Marker(\"\", coordinate:).tag(id)\n│   MKMapView: view.clusteringIdentifier = \"poi\"\n│\n└─ 1000+ → Server-side clustering or visible-region filtering\n    Fetch only annotations within mapView.region\n    Or pre-cluster on server, send cluster centroids\n    MKMapView with view reuse is preferred for very large datasets\n```\n\n#### Visible-Region Filtering (SwiftUI)\n\nOnly load annotations within the visible map region. Prevents loading all 10K+ annotations at once:\n\n```swift\nstruct MapView: View {\n    @State private var cameraPosition: MapCameraPosition = .automatic\n    @State private var visibleAnnotations: [Location] = []\n\n    let allLocations: [Location]  \u002F\u002F Full dataset\n\n    var body: some View {\n        Map(position: $cameraPosition) {\n            ForEach(visibleAnnotations) { location in\n                Marker(location.name, coordinate: location.coordinate)\n            }\n        }\n        .onMapCameraChange(frequency: .onEnd) { context in\n            visibleAnnotations = allLocations.filter { location in\n                context.region.contains(location.coordinate)\n            }\n        }\n    }\n}\n\nextension MKCoordinateRegion {\n    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool {\n        let latRange = (center.latitude - span.latitudeDelta \u002F 2)...(center.latitude + span.latitudeDelta \u002F 2)\n        let lngRange = (center.longitude - span.longitudeDelta \u002F 2)...(center.longitude + span.longitudeDelta \u002F 2)\n        return latRange.contains(coordinate.latitude) && lngRange.contains(coordinate.longitude)\n    }\n}\n```\n\n#### Why Clustering Matters\n\nWithout clustering at 500 annotations:\n- Map is unreadable (pins overlap completely)\n- Scroll\u002Fzoom lag increases with every annotation\n- Memory grows linearly with annotation count\n\nWith clustering:\n- User sees meaningful groups with counts\n- Only visible cluster markers rendered\n- Tap to expand reveals individual annotations\n\n### Decision Tree 3: Search and Directions\n\n```\nSearch implementation:\n├─ User types search query\n│   └─ MKLocalSearchCompleter (real-time autocomplete)\n│       Configure: resultTypes, region bias\n│       └─ User selects result\n│           └─ MKLocalSearch (full result with MKMapItem)\n│               Use completion.title for MKLocalSearch.Request\n│\n└─ Programmatic search (e.g., \"nearest gas station\")\n    └─ MKLocalSearch with naturalLanguageQuery\n        Configure: resultTypes, region, pointOfInterestFilter\n\nDirections implementation:\n├─ MKDirections.Request\n│   Set source (MKMapItem.forCurrentLocation()) and destination\n│   Set transportType (.automobile, .walking, .transit)\n│\n└─ MKDirections.calculate()\n    └─ MKRoute\n        ├─ .polyline → Display as MapPolyline or MKPolylineRenderer\n        ├─ .expectedTravelTime → Show ETA\n        ├─ .distance → Show distance\n        └─ .steps → Turn-by-turn instructions\n```\n\n---\n\n## Part 3: Pressure Scenarios\n\n### Scenario 1: \"Just Wrap MKMapView in UIViewRepresentable\"\n\n**Setup**: Adding a map to a SwiftUI app. Developer is familiar with MKMapView from UIKit projects.\n\n**Pressure**: \"I know MKMapView well. SwiftUI Map is new and might be limited.\"\n\n**Expected with skill**: Check the decision tree. If the app needs standard markers, annotations, camera control, user location, and shape overlays — SwiftUI Map handles all of that. Use it.\n\n**Anti-pattern without skill**: 200+ lines of UIViewRepresentable + Coordinator wrapping MKMapView, manually bridging state, implementing delegate methods for annotation views, fighting updateUIView infinite loops — when 20 lines of `Map {}` with content builder would have worked.\n\n**Time cost**: 2-4 hours of unnecessary boilerplate + ongoing maintenance burden.\n\n**The test**: Can you list a specific feature the app needs that SwiftUI Map cannot provide? If not, use SwiftUI Map.\n\n### Scenario 2: \"Add All 10,000 Pins to the Map\"\n\n**Setup**: App has a database of 10,000 location data points. Product manager wants users to see all locations on the map.\n\n**Pressure**: \"Users need to see ALL locations. Just add them all.\"\n\n**Expected with skill**: Use clustering + visible region filtering. 10K annotations without clustering is unusable — pins overlap, scrolling lags, memory spikes. Clustering shows meaningful groups. Visible region filtering loads only what's on screen.\n\n**Anti-pattern without skill**: Adding all 10,000 annotations at once. Map becomes an unreadable blob of overlapping pins. Scroll lag makes the app feel broken. Memory usage spikes 200-400MB.\n\n**Implementation path**:\n1. Enable clustering (`.clusteringIdentifier`)\n2. Fetch annotations only within visible region (`.onMapCameraChange` + query)\n3. Server-side pre-clustering for datasets > 5K if possible\n\n### Scenario 3: \"Search Isn't Finding Results\"\n\n**Setup**: MKLocalSearch returns irrelevant or empty results. Developer considers adding Google Maps SDK.\n\n**Pressure**: \"MapKit search is broken. Let me add a third-party SDK.\"\n\n**Expected with skill**: Check configuration first. MapKit search needs:\n1. `resultTypes` — filter to `.pointOfInterest` or `.address` (default returns everything)\n2. `region` — bias results to the visible map region\n3. Query format — natural language like \"coffee shops\" works; structured queries don't\n\n**Anti-pattern without skill**: Adding Google Maps SDK (50+ MB binary, API key management, billing setup) when MapKit search works correctly with proper configuration.\n\n**Time cost**: 4-8 hours adding third-party SDK vs 5 minutes configuring MapKit search.\n\n---\n\n## Part 4: Core Location Integration\n\nMapKit and Core Location interact in ways that surprise developers.\n\n### Implicit Authorization\n\nWhen you set `showsUserLocation = true` on MKMapView or add `UserAnnotation()` in SwiftUI Map, MapKit implicitly requests location authorization if it hasn't been requested yet.\n\nThis means:\n- The authorization prompt appears at map display time, not when the developer expects\n- The user sees a prompt with no context about why location is needed\n- If denied, the blue dot silently doesn't appear\n\n#### Recommended Pattern\n\nRequest authorization explicitly BEFORE showing the map:\n\n```swift\n\u002F\u002F 1. Request authorization with context\nlet session = CLServiceSession(authorization: .whenInUse)\n\n\u002F\u002F 2. Then show map with user location\nMap {\n    UserAnnotation()\n}\n```\n\n### CLServiceSession (iOS 17+)\n\nFor continuous location display on a map, create a `CLServiceSession`:\n\n```swift\n@Observable\nclass MapModel {\n    var cameraPosition: MapCameraPosition = .automatic\n    private var locationSession: CLServiceSession?\n\n    func startShowingUserLocation() {\n        locationSession = CLServiceSession(authorization: .whenInUse)\n    }\n\n    func stopShowingUserLocation() {\n        locationSession = nil\n    }\n}\n```\n\n### Cross-Reference\n\nFor full authorization decision trees, monitoring patterns, and background location:\n- `axiom-core-location` — Authorization strategy, monitoring approach\n- `axiom-core-location-diag` — \"Location not working\" troubleshooting\n- `axiom-energy` — Location as battery subsystem\n\n---\n\n## Part 5: SwiftUI Map Quick Start\n\nThe most common pattern — a map with markers and user location:\n\n```swift\nstruct ContentView: View {\n    @State private var cameraPosition: MapCameraPosition = .automatic\n    @State private var selectedItem: MKMapItem?\n\n    let locations: [Location]  \u002F\u002F Your model\n\n    var body: some View {\n        Map(position: $cameraPosition, selection: $selectedItem) {\n            UserAnnotation()\n\n            ForEach(locations) { location in\n                Marker(location.name, coordinate: location.coordinate)\n                    .tint(location.category.color)\n            }\n        }\n        .mapStyle(.standard(elevation: .realistic))\n        .mapControls {\n            MapUserLocationButton()\n            MapCompass()\n            MapScaleView()\n        }\n        .onChange(of: selectedItem) { _, item in\n            if let item {\n                handleSelection(item)\n            }\n        }\n    }\n}\n```\n\n#### Key Points\n\n- `@State var cameraPosition` — bind for programmatic camera control\n- `selection: $selectedItem` — handle tap on markers\n- `MapCameraPosition.automatic` — system manages initial view\n- `.mapControls {}` — built-in UI for location button, compass, scale\n- `ForEach` in content builder — dynamic annotations from data\n\n---\n\n## Part 6: Search Implementation Pattern\n\nComplete search with autocomplete:\n\n```swift\n@Observable\nclass SearchModel {\n    var searchText = \"\"\n    var completions: [MKLocalSearchCompletion] = []\n    var searchResults: [MKMapItem] = []\n\n    private let completer = MKLocalSearchCompleter()\n    private var completerDelegate: CompleterDelegate?\n\n    init() {\n        completerDelegate = CompleterDelegate { [weak self] results in\n            self?.completions = results\n        }\n        completer.delegate = completerDelegate\n        completer.resultTypes = [.pointOfInterest, .address]\n    }\n\n    func updateSearch(_ text: String) {\n        searchText = text\n        completer.queryFragment = text\n    }\n\n    func search(for completion: MKLocalSearchCompletion) async throws {\n        let request = MKLocalSearch.Request(completion: completion)\n        request.resultTypes = [.pointOfInterest, .address]\n        let search = MKLocalSearch(request: request)\n        let response = try await search.start()\n        searchResults = response.mapItems\n    }\n\n    func search(query: String, in region: MKCoordinateRegion) async throws {\n        let request = MKLocalSearch.Request()\n        request.naturalLanguageQuery = query\n        request.region = region\n        request.resultTypes = .pointOfInterest\n        let search = MKLocalSearch(request: request)\n        let response = try await search.start()\n        searchResults = response.mapItems\n    }\n}\n```\n\n#### MKLocalSearchCompleter Delegate (Required)\n\n```swift\nclass CompleterDelegate: NSObject, MKLocalSearchCompleterDelegate {\n    let onUpdate: ([MKLocalSearchCompletion]) -> Void\n\n    init(onUpdate: @escaping ([MKLocalSearchCompletion]) -> Void) {\n        self.onUpdate = onUpdate\n    }\n\n    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {\n        onUpdate(completer.results)\n    }\n\n    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {\n        \u002F\u002F Handle error — network issues, rate limiting\n    }\n}\n```\n\n#### Rate Limiting\n\nApple rate-limits MapKit search. For autocomplete:\n- `MKLocalSearchCompleter` handles its own throttling internally\n- Don't create a new completer per keystroke — reuse one instance\n- Set `queryFragment` on each keystroke; the completer debounces\n\nFor `MKLocalSearch`:\n- Don't fire a search on every keystroke — use the completer for autocomplete\n- Fire `MKLocalSearch` only when the user selects a completion or submits\n\n---\n\n## Part 7: Directions Implementation Pattern\n\n```swift\nfunc calculateDirections(\n    from source: CLLocationCoordinate2D,\n    to destination: MKMapItem,\n    transportType: MKDirectionsTransportType = .automobile\n) async throws -> MKRoute {\n    let request = MKDirections.Request()\n    request.source = MKMapItem(placemark: MKPlacemark(coordinate: source))\n    request.destination = destination\n    request.transportType = transportType\n\n    let directions = MKDirections(request: request)\n    let response = try await directions.calculate()\n\n    guard let route = response.routes.first else {\n        throw MapError.noRouteFound\n    }\n    return route\n}\n```\n\n#### Displaying the Route (SwiftUI)\n\n```swift\nMap(position: $cameraPosition) {\n    if let route {\n        MapPolyline(route.polyline)\n            .stroke(.blue, lineWidth: 5)\n    }\n\n    Marker(\"Start\", coordinate: startCoord)\n    Marker(\"End\", coordinate: endCoord)\n}\n```\n\n#### Displaying the Route (MKMapView)\n\n```swift\n\u002F\u002F Add overlay\nmapView.addOverlay(route.polyline, level: .aboveRoads)\n\n\u002F\u002F Implement renderer delegate\nfunc mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {\n    if let polyline = overlay as? MKPolyline {\n        let renderer = MKPolylineRenderer(polyline: polyline)\n        renderer.strokeColor = .systemBlue\n        renderer.lineWidth = 5\n        return renderer\n    }\n    return MKOverlayRenderer(overlay: overlay)\n}\n```\n\n#### Route Information\n\n```swift\nlet route: MKRoute = ...\nlet travelTime = route.expectedTravelTime  \u002F\u002F TimeInterval in seconds\nlet distance = route.distance              \u002F\u002F CLLocationDistance in meters\nlet steps = route.steps                    \u002F\u002F [MKRoute.Step]\n\nfor step in steps {\n    print(\"\\(step.instructions) — \\(step.distance)m\")\n    \u002F\u002F \"Turn right on Main St — 450m\"\n}\n```\n\n---\n\n## Part 8: Clustering Pattern\n\n### SwiftUI (iOS 17+)\n\n```swift\nMap(position: $cameraPosition) {\n    ForEach(locations) { location in\n        Marker(location.name, coordinate: location.coordinate)\n            .tag(location.id)\n    }\n    .mapItemClusteringIdentifier(\"locations\")\n}\n```\n\n### MKMapView\n\n```swift\nfunc mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {\n    if let cluster = annotation as? MKClusterAnnotation {\n        let view = mapView.dequeueReusableAnnotationView(\n            withIdentifier: \"cluster\",\n            for: annotation\n        ) as! MKMarkerAnnotationView\n        view.markerTintColor = .systemBlue\n        view.glyphText = \"\\(cluster.memberAnnotations.count)\"\n        return view\n    }\n\n    let view = mapView.dequeueReusableAnnotationView(\n        withIdentifier: \"pin\",\n        for: annotation\n    ) as! MKMarkerAnnotationView\n    view.clusteringIdentifier = \"locations\"\n    view.markerTintColor = .systemRed\n    return view\n}\n```\n\n#### Clustering Requirements\n\n1. All annotation views that should cluster MUST share the same `clusteringIdentifier`\n2. Register annotation view classes: `mapView.register(MKMarkerAnnotationView.self, forAnnotationViewWithReuseIdentifier: \"pin\")`\n3. Clustering only activates when annotations physically overlap at the current zoom level\n4. System manages cluster\u002Funcluster animation automatically\n\n---\n\n## Part 9: Pre-Release Checklist\n\n- [ ] Map loads and displays correctly\n- [ ] Annotations appear at correct coordinates (lat\u002Flng not swapped)\n- [ ] Clustering works with 100+ annotations\n- [ ] Search returns relevant results (resultTypes configured)\n- [ ] Camera position controllable programmatically\n- [ ] Memory stable when scrolling\u002Fzooming with many annotations\n- [ ] User location shows correctly (authorization handled before display)\n- [ ] Directions render as polyline overlay\n- [ ] Map works in Dark Mode (map styles adapt automatically)\n- [ ] Accessibility: VoiceOver announces map elements\n- [ ] No setRegion\u002FupdateUIView infinite loops (if using MKMapView)\n- [ ] MKLocalSearchCompleter reused (not recreated per keystroke)\n- [ ] Annotation views reused via `dequeueReusableAnnotationView` (MKMapView)\n- [ ] Look Around availability checked before displaying (`MKLookAroundSceneRequest`)\n\n---\n\n## Resources\n\n**WWDC**: 2023-10043, 2024-10094\n\n**Docs**: \u002Fmapkit, \u002Fmapkit\u002Fmap\n\n**Skills**: mapkit-ref, mapkit-diag, core-location\n\n",{"name":5,"license":15,"metadata":36,"description":7},{"version":37,"last-updated":38},"1.0.0","2026-02-26","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-mapkit",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill axiom-mapkit",{"tool":54,"command":55},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill axiom-mapkit",{"tool":57,"command":58},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill axiom-mapkit","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002FCharlesWiltgen\u002FAxiom\u002Faxiom-mapkit\u002FSKILL.md",true,[62,68,74],{"path":63,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":66,"sourceHtmlUrl":67},"references\u002Fissue-taxonomy.md","REFERENCE","参考文档",3619,"https:\u002F\u002Fgithub.com\u002Fmarshadgani\u002FArshad.AI\u002Fblob\u002Fclaude\u002Fai-personal-assistant-main\u002F.claude\u002Fskills\u002Fgstack\u002Fqa\u002Freferences\u002Fissue-taxonomy.md",{"path":69,"resourceType":70,"resourceTypeDesc":71,"sizeBytes":72,"sourceHtmlUrl":73},"SKILL.md.tmpl","OTHER","其他",12701,"https:\u002F\u002Fgithub.com\u002Fmarshadgani\u002FArshad.AI\u002Fblob\u002Fclaude\u002Fai-personal-assistant-main\u002F.claude\u002Fskills\u002Fgstack\u002Fqa\u002FSKILL.md.tmpl",{"path":75,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":76,"sourceHtmlUrl":77},"templates\u002Fqa-report-template.md",2979,"https:\u002F\u002Fgithub.com\u002Fmarshadgani\u002FArshad.AI\u002Fblob\u002Fclaude\u002Fai-personal-assistant-main\u002F.claude\u002Fskills\u002Fgstack\u002Fqa\u002Ftemplates\u002Fqa-report-template.md",[79,93,103,112,118,125],{"skillNo":80,"name":81,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":89,"publishedAt":31},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[84],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[90,91,92],"model","model hierarchy","model hierarchy skill",{"skillNo":94,"name":95,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":99,"publishedAt":31},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[98],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},[100,101,102],"MCP","zoom","zoom mcp",{"skillNo":104,"name":105,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":31},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[109,110,111,101],"sdk","zoom apps sdk","apps",{"skillNo":113,"name":114,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":31},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[114],{"skillNo":119,"name":120,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":124,"publishedAt":31},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[123],{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},[120],{"skillNo":126,"name":127,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":130,"publishedAt":31},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[127]]