AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Mapsgl Apple

skill-vaisala-xweather-xweather-agent-skills-mapsgl-apple · by vaisala-xweather

This skill should be used when working with the Xweather MapsGL SDK for Apple platforms (the MapsGL iOS/iPadOS/macCatalyst/visionOS SDK) — installing it via Swift Package Manager, CocoaPods, Carthage or xcframeworks, creating a MapboxMapController or MapLibreMapController, and adding, removing, styling, animating or inspecting MapsGL weather layers in Swift. Use it whenever a task mentions MapsGL…

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-vaisala-xweather-xweather-agent-skills-mapsgl-apple

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-vaisala-xweather-xweather-agent-skills-mapsgl-apple)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Mapsgl Apple? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

MapsGL for Apple platforms

The Xweather MapsGL SDK for Apple platforms renders weather and custom map data client-side with Metal, layered on top of Mapbox Maps or MapLibre Native. It requires an active Xweather account with Weather API + Maps access (client id + secret).

Platform support comes from the package manifest: iOS 16+, macCatalyst 16+, visionOS 1+. There is no native macOS (AppKit) target — a "macOS" app here means Mac Catalyst.

Docs: https://www.xweather.com/docs/mapsgl-apple-sdk/getting-started · Distribution + demo app: https://github.com/vaisala-xweather/mapsgl-apple-sdk

Ask which map provider unless the context tells you

The map provider is not a stylistic preference that can be defaulted: Mapbox and MapLibre resolve different Swift Package branches, different transitive SDKs, and different map-view types. Getting it wrong means the code doesn't compile and the package graph has to be redone.

So infer it when the context actually says, and ask when it doesn't. Never pick one by default.

Infer it from evidence like:

  • the user named the provider in their request;
  • the project already integrates one — an import MapsGLMapbox / import MapsGLMapLibre, a resolved

mapbox-maps-ios or maplibre-gl-native-distribution dependency, a Podfile naming one, an MLNMapView or MapboxMaps.MapView in the source, or a MBXAccessToken in an Info.plist;

  • the project already uses the provider's SDK elsewhere, even without MapsGL — a Mapbox-based map

screen means Mapbox.

When you infer, say which provider you picked and what told you, so a wrong read is cheap to correct.

Ask when the evidence is absent or contradictory — a greenfield app, a project with no map dependency yet, or one carrying traces of both. Weak circumstantial signals ("we want a dark map style", "our designer sent Mapbox screenshots") are not evidence of an integration; ask rather than build the whole package graph on them.

Trade-offs to offer alongside the question, briefly:

| | Mapbox Maps | MapLibre Native | |---|---|---| | Basemap key | Mapbox account access token required, plus a secret token to download the SDK | None — but the style URL's tile provider may need one (CARTO's public styles don't) | | Cost | Mapbox map-load pricing applies | No basemap vendor cost | | SwiftUI | Native Map view | MLNMapView wrapped in a UIViewRepresentable | | MapsGL constraint | Must set the mercator projection — the default globe projection is incompatible | None |

How to write MapsGL Apple examples

Default to SwiftUI. Produce UIKit only when the project is UIKit (a UIViewController-based app, storyboards/XIBs, an AppDelegate/SceneDelegate pair with no SwiftUI App) or the user asks for it. Match the surrounding project over the default whenever the two disagree — including Swift concurrency style, view-model conventions, and how the app already stores secrets.

Never hardcode a version number. Resolve the current release when you need one:

curl -s https://www.xweather.com/docs/api/releases/versions \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["products"]["mapsgl-apple-sdk"]["version"])'

That endpoint is the release source of truth for every Xweather product, keyed by product id — mapsgl-apple-sdk here, alongside mapsgl, weather-api, maps, and others. It's a small public JSON document, no auth needed.

Most of the time you don't need a version at all: prefer the Swift Package branch channels (below), which track the latest release without a pin. A version is only needed for a deliberate pin, a CocoaPods/Carthage requirement, or an API-reference URL.

Credentials never go in source. Put the Xweather client id/secret and any Mapbox token in a gitignored plist, xcconfig, or the keychain — whatever the project already uses — and read them at runtime. The demo app's AccessKeys.plist pattern is a reasonable model when the project has none. Write "FILL_IN_WITH_YOUR_CLIENT_ID"-style placeholders rather than inventing plausible keys.

Every example must include the Xweather attribution. It's a requirement of using the product, not a nicety, so build it into the view rather than mentioning it afterwards. See "Attribution is required".

API reference

The full API reference is DocC, published per SDK version:

https://cdn.aerisapi.com/sdk/ios/mapsgl/docs/v{version}/documentation/mapsglmaps

Substitute the version from the releases endpoint above — there is no latest alias; .../docs/latest/... 404s. Only the mapsglmaps module is published; MapsGLCore, MapsGLRenderer, and the two adapter modules have no hosted DocC.

The version index page, which lists every published version, is https://www.xweather.com/docs/mapsgl-apple-sdk/api-reference.

A machine-readable symbol index sits alongside it at https://cdn.aerisapi.com/sdk/ios/mapsgl/docs/v{version}/index/index.json — useful for checking whether a symbol exists in a given release before writing code against it.

references/api-reference.md carries the surface an agent needs most (controller, service, timeline, controls, descriptors) so the common cases need no network call.

Core concepts

| Concept | What it is | |---|---| | XweatherAccount(id:secret:) | Wraps client id/secret credentials used for all data requests | | MapController | Adapter between the underlying map (MapboxMaps.MapboxMap / MLNMapView) and MapsGL — the object almost everything below is called on. Concrete: MapboxMapController, MapLibreMapController | | WeatherService | The account-bound weather data service, reachable as controller.service. Namespaces every built-in layer configuration and WeatherService.LayerCode | | WeatherService.LayerCode | Enum identifying a built-in weather layer — .radar, .temperatures, .windParticles | | WeatherService. | Per-layer configuration struct (WeatherService.Temperatures) holding layer, legend, and presentation. Instantiate it to override defaults | | Source descriptors | Where custom layer data comes from — ImageSourceDescriptor, EncodedSourceDescriptor, VectorSourceDescriptor, GeoJSONSourceDescriptor | | Layer descriptors | How data is rendered — RasterLayerDescriptor, SampleLayerDescriptor, ParticleLayerDescriptor, GridLayerDescriptor, ContourLayerDescriptor, FillLayerDescriptor, LineLayerDescriptor, CircleLayerDescriptor, SymbolLayerDescriptor, HeatmapLayerDescriptor | | paint | Per-descriptor style config, namespaced by render type — paint.sample, paint.fill, paint.stroke. See references/styles.md | | Expression | Data-driven paint values and layer filters, built with static factories — Expression.get("COLOR"). See references/expressions.md | | ColorScaleOptions / ColorStop | Maps a continuous data range to colors, used by paint.sample.colorScale and bar legends | | LegendControl | Manages the legends visible on a map; auto-syncs with built-in weather layers. See references/legends.md | | DataInspectorControl | Tap-to-inspect callout showing raw layer values at a coordinate | | controller.timeline | Drives time animation across every animated layer at once. See references/timeline.md |

Built-in weather layers are pre-wired combinations of a source + styled layer(s), addressed by a single LayerCode case. Prefer these over hand-building sources and layers unless visualizing custom or non-weather data.

Setup

1. Credentials

Two independent sets, both required:

  1. Xweather account keysCLIENT_ID / CLIENT_SECRET from

https://data.portal.xweather.com/account/keys. Passed as XweatherAccount(id:secret:).

  1. The map provider's own credentials
  • Mapbox → a public access token set on MapboxOptions.accessToken, plus a secret download

token configured in ~/.netrc so SPM/CocoaPods can fetch the Mapbox SDK at all. The secret token is a build-time requirement; forgetting it fails resolution, not runtime.

  • MapLibre → nothing for the SDK. The basemap styleURL points at a tile provider, which may need

its own key (CARTO's public Positron/Dark Matter styles do not).

If nothing renders, check both sets before investigating MapsGL.

2. Install

Swift Package Manager (preferred). Add https://github.com/vaisala-xweather/mapsgl-apple-sdk and pick the branch matching your provider — the package manifest at the repo root is provider-specific per branch, so the branch is the provider choice:

| Channel | Branch | Resolves | Product | |---|---|---|---| | Latest Mapbox | master | mapbox-maps-ios 11.x | MapsGL | | Latest MapLibre | maplibre | maplibre-gl-native-distribution 6.18+ | MapsGL | | Pinned Mapbox | release/x.y.z | as above, frozen | MapsGL | | Pinned MapLibre | release/maplibre/x.y.z | as above, frozen | MapsGL |

The product name is MapsGL on every branch; what differs is which adapter target it includes. Add the MapsGL library product to the app target. Xcode resolves the three binary xcframeworks (MapsGLCore, MapsGLRenderer, MapsGLMaps) plus the provider SDK and turf-swift automatically.

Use a branch channel unless the user asked to pin. Pinning to release/… is the right call for release-managed apps; note that it also freezes the provider SDK range.

CocoaPodspod 'MapsGL', then pod install and open the generated .xcworkspace. CocoaPods builds a single MapsGL module, so import MapsGL replaces the adapter import (import MapsGLMapbox / import MapsGLMapLibre) in every source file. This is the most common CocoaPods build error.

Carthage (github "vaisala-xweather/mapsgl-apple-sdk" ~> x.y.z, then carthage update --use-xcframeworks) and manual xcframework embedding (download MapsGL.zip from the releases page, embed the three xcframeworks as "Embed & Sign") both require adding the provider SDK yourself and dropping the matching adapter source directory (MapsGLMapbox/ or MapsGLMapLibre/) straight into the project. When the adapter is compiled into your target that way, remove the import MapsGLMapbox / import MapsGLMapLibre lines — the types are already in your module.

3. Imports

import MapsGLMaps      // always
import MapsGLMapbox    // SPM, Mapbox channel   — omit for CocoaPods/Carthage/manual
import MapsGLMapLibre  // SPM, MapLibre channel — omit for CocoaPods/Carthage/manual
import MapsGL          // CocoaPods only, in place of the adapter import
import Combine         // controller events return AnyCancellable

4. Create the controller, then wait for load

let account = XweatherAccount(id: clientID, secret: clientSecret)
let controller = MapboxMapController(map: map, account: account)

controller.onLoad.observe { _ in
    _ = try? controller.addWeatherLayer(for: .radar)
}.store(in: &cancellables)

| Provider | Controller | Map argument | |---|---|---| | Mapbox | MapboxMapController | MapboxMaps.MapView, or MapboxMaps.MapboxMap + window: | | MapLibre | MapLibreMapController | MLNMapView |

Every layer/source call must be gated behind load, and the observation returns an AnyCancellable you must retain — .store(in: &cancellables). Drop it and the observer is torn down immediately and the map stays empty, with no error.

Use onLoad.observe { … }. The older subscribe(to: MapEvents.Load.self) { … } is deprecated ("Use available on.observe() methods instead") — note that the web docs' SwiftUI sample still shows it, so copying from there warns on a new project. Same for asyncSubscribe and subscribeToNext; only publisher(for:) survives, for when you want Combine operators.

The layer and source API is @MainActor. addWeatherLayer, removeWeatherLayer, setWeatherLayerVisibility, weatherLayer(for:), addSource, addLayer, addImage, and add(legendControl:) are all main-actor-isolated. The onLoad observer already runs on the main thread, so the usual path needs nothing extra — but a call made from a detached task or a non-isolated callback needs await MainActor.run { … }, or it won't compile under strict concurrency.

Mapbox requires the mercator projection. The current Mapbox styles (streets/outdoor/satellite streets v12, light/dark v11) default to the globe projection, which MapsGL cannot render onto:

try map.setProjection(.init(name: .mercator))

Symptom when missing: the basemap draws normally and MapsGL layers simply never appear.

Complete example — SwiftUI

Mapbox. MapReader hands back a MapboxMap, so use the window:-taking initializer:

import SwiftUI
import Combine
import MapboxMaps
import MapsGLMaps
import MapsGLMapbox

struct WeatherMapView: View {
    // Read these from a gitignored plist / xcconfig / keychain — never commit them.
    private let xweatherClientID = "FILL_IN_WITH_YOUR_CLIENT_ID"
    private let xweatherClientSecret = "FILL_IN_WITH_YOUR_CLIENT_SECRET"

    final class Coordinator: ObservableObject {
        var controller: MapboxMapController?
        var cancellables: Set = []
    }
    @StateObject private var coordinator = Coordinator()

    var body: some View {
        MapReader { proxy in
            Map(initialViewport: .camera(
                center: CLLocationCoordinate2D(latitude: 39.65, longitude: -93.10),
                zoom: 3.5
            ))
            .mapStyle(.light)
            .ignoresSafeArea()
            .overlay(alignment: .bottomTrailing) { XweatherAttribution() }
            .onAppear {
                guard let map = proxy.map, coordinator.controller == nil else { return }

                // MapsGL cannot render onto Mapbox's default globe projection.
                try? map.setProjection(.init(name: .mercator))

                let controller = MapboxMapController(
                    map: map,
                    window: UIWindow?.none,
                    account: XweatherAccount(id: xweatherClientID, secret: xweatherClientSecret)
                )
                coordinator.controller = controller

                controller.onLoad.observe { _ in
                    do {
                        try controller.addWeatherLayer(for: .radar)

                        var winds = WeatherService.WindParticles(service: controller.service)
                        winds.layer.paint.particle.density = .high
                        try controller.addWeatherLayer(config: winds)
                    } catch {
                        NSLog("Failed to add weather layer: \(error)")
                    }
                }.store(in: &coordinator.cancellables)
            }
        }
    }
}

Set the Mapbox token once, before any map is created — in the App initializer or an @main type's init():

MapboxOptions.accessToken = "FILL_IN_WITH_YOUR_MAPBOX_PUBLIC_ACCESS_TOKEN"

MapLibre. MapLibre ships no SwiftUI view, so wrap MLNMapView. No token, and no projection call:

import SwiftUI
import Combine
import MapLibre
import MapsGLMaps
import MapsGLMapLibre

struct WeatherMapView: UIViewRepresentable {
    private let xweatherClientID = "FILL_IN_WITH_YOUR_CLIENT_ID"
    private let xweatherClientSecret = "FILL_IN_WITH_YOUR_CLIENT_SECRET"

    final class Coordinator {
        var controller: MapLibreMapController?
        var cancellables: Set = []
    }
    func makeCoordinator() -> Coordinator { Coordinator() }

    func makeUIView(context: Context) -> MLNMapView {
        let mapView = MLNMapView(frame: .zero)
        // Any MapLibre-compatible style. CARTO's public styles need no key.
        mapView.styleURL = URL(string: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json")!
        mapView.setCenter(
            CLLocationCoordinate2D(latitude: 39.65, longitude: -93.10),
            zoomLevel: 3.5,
            animated: false
        )

        let controller = MapLi

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [vaisala-xweather](https://github.com/vaisala-xweather)
- **Source:** [vaisala-xweather/xweather-agent-skills](https://github.com/vaisala-xweather/xweather-agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.