Query a feature layer (SQL)

Learn how to execute a SQL query to return from a based on spatial and criteria.

query a feature layer

A can contain a large number of stored in ArcGIS. You can query a layer to access a subset of its features using any combination of spatial and criteria. You can control whether or not each feature's is returned, as well as which attributes are included in the results. Queries allow you to return a well-defined subset of your hosted data for analysis or display in your ArcGIS Runtime app.

In this tutorial, you'll write code to perform SQL queries that return a subset of in the LA County Parcel (containing over 2.4 million features). Features that meet the query criteria are selected in the map.

Prerequisites

Before starting this tutorial:

  1. You need an ArcGIS Location Platform or ArcGIS Online account.

  2. Your system meets the system requirements.

Steps

Open the Xcode project

  1. To start the tutorial, complete the Display a map tutorial or download and unzip the solution.

  2. Open the .xcodeproj file in Xcode.

  3. If you downloaded the solution, get an access token and set the API key.

Set selection properties

Define the selection color via the map view's AGSSelectionProperties to distinguish selected features in the map view.

  1. In Xcode, in the Project Navigator, click ViewController.swift.

  2. Update the setupMap() method to set map view's selection color to be yellow. This step is optional, and if you don't set a selection color, cyan is used as the default.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private func setupMap() {
            mapView.map = map
            mapView.setViewpoint(
                AGSViewpoint(
                    latitude: 34.02700,
                    longitude: -118.80500,
                    scale: 72_000
                )
            )
    
            // Set selection color.
            mapView.selectionProperties.color = .yellow
    
        }
    

Add a parcels layer to the map

Access the LA parcels feature service and create a new layer to display those features in the map.

  1. In ViewController.swift, create a property for an AGSServiceFeatureTable to access LA parcels after the mapView property declaration.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        @IBOutlet var mapView: AGSMapView!
        private let map = AGSMap(basemapStyle: .arcGISTopographic)
    
        let featureTable = AGSServiceFeatureTable(url: URL(string: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0")!)
    
    
  2. Create a new method, setupLayer(). Create an AGSFeatureLayer and add it to the map's list of operational layers to view the parcels.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private func setupLayer() {
            // Add feature layer to map's operational layers.
            let parcelsFeatureLayer = AGSFeatureLayer(featureTable: featureTable)
            mapView.map?.operationalLayers.add(parcelsFeatureLayer)
        }
    
  3. Update the viewDidLoad() method with a call to setupLayer().

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        override func viewDidLoad() {
            super.viewDidLoad()
            setupMap()
    
            setupLayer()
    
        }
    
  4. Press Command + R to run the app.

The app loads with the map centered on the Santa Monica Mountains in California with the parcels feature layer displayed.

Create a UI control to display predefined query strings

To make performing a query on a feature layer flexible, add a UI control to present a list of predefined attribute queries for the parcels dataset.

  1. Create a UIButton and define its properties.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        @IBOutlet var mapView: AGSMapView!
        private let map = AGSMap(basemapStyle: .arcGISTopographic)
    
        let featureTable = AGSServiceFeatureTable(url: URL(string: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/LA_County_Parcels/FeatureServer/0")!)
    
        let optionsButton: UIButton = {
            let button = UIButton()
            button.backgroundColor = .lightGray
            button.setTitleColor(.blue, for: .normal)
            button.setTitle("Choose a SQL 'where' clause", for: .normal)
            button.translatesAutoresizingMaskIntoConstraints = false
            return button
        }()
    
  2. Create a new method, setupButton(), and add the button as a subView of the main view. This displays the button when the app is run.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private func setupButton() {
    
            // Add button as subview to the view.
            view.addSubview(optionsButton)
    
        }
    
  3. Next, add layout constraints to define the position of the button relative to the main view and safeArea of the device. This ensures the button looks as expected in different devices and orientations.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private func setupButton() {
    
            // Add button as subview to the view.
            view.addSubview(optionsButton)
    
            // Add layout constraints defining the position of the button
            optionsButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            optionsButton.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            optionsButton.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
            optionsButton.heightAnchor.constraint(equalToConstant: 60 + self.view.safeAreaInsets.bottom).isActive = true
    
        }
    
  4. Next, add a target for the button with the method to execute when it is tapped. This method, populateOptions(), which you'll create in the next section, populates and displays a list of query options.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        private func setupButton() {
    
            // Add button as subview to the view.
            view.addSubview(optionsButton)
    
            // Add layout constraints defining the position of the button
            optionsButton.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            optionsButton.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            optionsButton.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
            optionsButton.heightAnchor.constraint(equalToConstant: 60 + self.view.safeAreaInsets.bottom).isActive = true
    
            // Define method to call when button is tapped.
            optionsButton.addTarget(self, action: #selector(populateOptions), for: .touchUpInside)
    
        }
    
  5. Update the viewDidLoad() method with a call to setupButton().

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        override func viewDidLoad() {
            super.viewDidLoad()
            setupMap()
    
            setupLayer()
    
            setupButton()
    
        }
    

You can also create and set up the UIButton using storyboards.

Create predefined query expressions

You'll now create and present a list of query options for the user to choose from when the button is tapped.

  1. To populate the UI with a predefined list of where clauses for the query, create a new method called populateOptions().

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        @objc func populateOptions() {
    
        }
    
  2. Create an UIAlertController with a title and of style actionSheet. Create an array of strings to populate the where clauses. Create an alert action for each where clause, which calls the method to execute the query when chosen. You'll implement the method executeQuery(whereClause:) later.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        @objc func populateOptions() {
    
            // Populate query options.
            let alert = UIAlertController(title: "Choose a SQL where clause.", message: nil, preferredStyle: .actionSheet)
            let whereClauses = ["UseType = 'Government'", "UseType = 'Residential'", "UseType = 'Irrigated Farm'", "TaxRateArea = 10853", "TaxRateArea = 10860", "Roll_LandValue > 1000000", "Roll_LandValue < 1000000"]
            whereClauses.forEach { whereClause in
                let action = UIAlertAction(title: whereClause, style: .default) { [unowned self] _ in
                    executeQuery(whereClause: whereClause)
                }
                alert.addAction(action)
            }
    
        }
    
  3. Add code to present the options to the user.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        @objc func populateOptions() {
    
            // Populate query options.
            let alert = UIAlertController(title: "Choose a SQL where clause.", message: nil, preferredStyle: .actionSheet)
            let whereClauses = ["UseType = 'Government'", "UseType = 'Residential'", "UseType = 'Irrigated Farm'", "TaxRateArea = 10853", "TaxRateArea = 10860", "Roll_LandValue > 1000000", "Roll_LandValue < 1000000"]
            whereClauses.forEach { whereClause in
                let action = UIAlertAction(title: whereClause, style: .default) { [unowned self] _ in
                    executeQuery(whereClause: whereClause)
                }
                alert.addAction(action)
            }
    
            // Present query options.
            alert.popoverPresentationController?.sourceView = optionsButton
            alert.popoverPresentationController?.sourceRect = optionsButton.frame
            alert.preferredContentSize = CGSize(width: 300, height: 200)
            present(alert, animated: true)
    
        }
    

Add a method to execute the query

In this step, create a new method that queries the parcels AGSFeatureLayer using both attribute and spatial criteria. After clearing any currently selected features, a new query will be executed to find features in the map's current extent that meet the selected attribute expression. The features in the AGSFeatureQueryResult will be selected in the parcels layer.

  1. Add the executeQuery function to query parcels using a provided where expression.

    ViewController.swift
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
        func executeQuery(whereClause: String) {
            // Get the parcels layer from the map
            guard let parcelsFeatureLayer = map.operationalLayers.firstObject as? AGSFeatureLayer else {
                //... handle error
                return
            }
    
            // Clear any existing selection.
            parcelsFeatureLayer.clearSelection()
    
            // Create the query parameters using the where expression and extent passed in.
            let queryParams = AGSQueryParameters()
            queryParams.whereClause = whereClause
            queryParams.geometry = mapView.visibleArea?.extent
            queryParams.returnGeometry = true
    
            featureTable.queryFeatures(with: queryParams) { queryResult, error in
                if let _ = error {
                    //... handle error
                } else if let features = queryResult?.featureEnumerator().allObjects as? [AGSArcGISFeature] {
                    parcelsFeatureLayer.select(features)
                }
            }
        }
    

Run the app

  1. Press Command + R to run the app.

  2. The app loads with the map centered on the Santa Monica Mountains in California with the parcels feature layer displayed. Choose an attribute expression, and parcels in the current extent that meet the selected criteria will display in the specified selection color.

What's next?

Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials:

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.

You can no longer sign into this site. Go to your ArcGIS portal or the ArcGIS Location Platform dashboard to perform management tasks.

Your ArcGIS portal

Create, manage, and access API keys and OAuth 2.0 developer credentials, hosted layers, and data services.

Your ArcGIS Location Platform dashboard

Manage billing, monitor service usage, and access additional resources.

Learn more about these changes in the What's new in Esri Developers June 2024 blog post.

Close