# Working with Custom Fields Filters

    library(sensortowerR)
    library(dplyr)

## Introduction

Custom Fields Filters are a powerful feature in Sensor Tower that let you create complex, reusable queries to segment apps across hundreds of attributes. In `sensortowerR` 1.0.0, every filter is built with a single verb — `st_filter()` — which returns an S3 object accepted anywhere a raw filter ID string was accepted before.

## Understanding Custom Fields

Sensor Tower tracks over 400 custom fields, including:

- Game attributes: Genre, sub-genre, art style, camera POV
- Monetization: Free/paid, IAP, ads, subscriptions
- Technical: SDKs used, platform support, device compatibility
- Performance: Retention rates, ratings, download counts
- Metadata: Release dates, publisher info, update frequency

## Basic workflow

### 1. Discover available fields

    # List every custom field you can filter on
    fields <- st_discover_fields()

    # See valid values for a specific field
    st_custom_fields_values("Primary Genre")

### 2. Build a filter

    # Single-criterion filter
    puzzle_filter <- st_filter(genre = "Puzzle")

    # Multi-criterion filter (all AND-combined)
    word_puzzle_free <- st_filter(
      genre        = c("Puzzle", "Word"),
      monetization = "Free"
    )

    # Date range
    new_releases <- st_filter(
      date_from = Sys.Date() - 90,
      date_to   = Sys.Date()
    )

    # SDK filter
    unity_games <- st_filter(sdk = "Unity")

    # Publisher filter
    ea_games <- st_filter(publisher = "Electronic Arts")

### 3. Inspect the filter

    print(puzzle_filter)
    as.character(puzzle_filter)  # Server-side filter ID

### 4. Use the filter

Every function that accepts a filter takes either a raw 24-hex ID string or an `st_filter` object:

    # Discover apps matching the filter
    puzzle_apps <- st_apps(filter = puzzle_filter, limit = 100)

    # Top-chart rankings restricted to the filter
    top_puzzle <- st_rankings(
      entity = "app",
      os     = "unified",
      filter = puzzle_filter,
      limit  = 25
    )

    # Raw list of filtered apps (power-user)
    raw_hits <- st_get_filtered_apps(filter = puzzle_filter)

### 5. Combine filters

Two `st_filter` objects compose with `c()`:

    rpgs       <- st_filter(genre = "RPG")
    free_apps  <- st_filter(monetization = "Free")
    free_rpgs  <- c(rpgs, free_apps)

    apps <- st_apps(filter = free_rpgs)

### 6. Reuse an existing filter ID

If you already have a filter ID from the web UI or a previous session:

    existing <- st_filter(filter_id = "5ba4585f539ce75b97db6bcb")
    st_apps(filter = existing)

## Advanced: arbitrary custom fields

For fields not covered by the named arguments, use `custom_fields = list(...)`:

    complex_filter <- st_filter(
      custom_fields = list(
        "Art Style"       = "3D",
        "Player Count"    = "Multiplayer",
        "Primary Genre"   = "Shooter"
      )
    )

    top_shooters <- st_rankings(
      entity = "app",
      os     = "ios",
      filter = complex_filter,
      limit  = 50
    )

## Inspecting and validating filters

    # Does a string look like a valid filter ID?
    st_is_valid_filter_id("5ba4585f539ce75b97db6bcb")  # TRUE

    # Fetch the canonical filter definition from the API
    collection <- st_get_filter_collection("5ba4585f539ce75b97db6bcb")

    # Run a smoke-test that the filter yields results
    st_test_filter("5ba4585f539ce75b97db6bcb")

    # Richer diagnostic output
    st_analyze_filter("5ba4585f539ce75b97db6bcb", verbose = TRUE)

## Migration from 0.9.x

If you were using `st_filter_by_date()`, `st_filter_by_genre()`, `st_custom_fields_filter()`, `st_combine_filters()`, etc. — all of those are now `.Defunct()` stubs that point at the correct `st_filter()` call:

    # Old (0.9.x)
    # puzzle <- st_filter_by_genre(genres = "Puzzle")
    # decade <- st_filter_by_date(start_date = "2020-01-01", end_date = "2029-12-31")
    # combined <- st_combine_filters(list(puzzle, decade))

    # New (1.0.0)
    puzzle   <- st_filter(genre = "Puzzle")
    decade   <- st_filter(date_from = "2020-01-01", date_to = "2029-12-31")
    combined <- c(puzzle, decade)

See `vignette("migrating-to-1.0", package = "sensortowerR")` for the complete translation table.