Skip to content

Query related features

View on GitHub

List features related to the selected feature.

Image of Query related features sample

Use case

Related features are useful for managing relational information, like what you would store in a relational database management system (RDBMS). You can define relationship between records as one-to-one, one-to-many, or many-to-one. For example, you could model inspections and facilities as a many-to-one relationship. Then, for any facility feature, you could list related inspection features.

How to use the sample

Tap on a feature to select it, and the related features will be displayed in a list.

How it works

  1. With a ArcGISFeature, call ArcGISFeatureTable.queryRelatedFeatures(to:using:) on the feature's feature table.
  2. Iterate over the result's collection of RelatedFeatureQueryResult objects to get the related features and add them to a list.

Relevant API

  • ArcGISFeature
  • ArcGISFeatureTable
  • FeatureQueryResult
  • RelatedFeatureQueryResult

Tags

features, identify, query, related, relationship, search

Sample Code

QueryRelatedFeaturesView.swift
Use dark colors for code blocksCopy
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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 // Copyright 2024 Esri // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.  import ArcGIS import SwiftUI  struct QueryRelatedFeaturesView: View {  /// A map with a topographic basemap.  @State private var map: Map = {  let map = Map(basemapStyle: .arcGISTopographic)   // Creates and adds feature tables to the map to allow related feature querying.  let preservesFeatureTable = ServiceFeatureTable(  url: .alaskaParksFeatureServer.appending(component: "0")  )  let speciesFeatureTable = ServiceFeatureTable(  url: .alaskaParksFeatureServer.appending(component: "2")  )  map.addTables([preservesFeatureTable, speciesFeatureTable])   return map  }()   /// An "Alaska National Parks" feature layer.  @State private var parksFeatureLayer = FeatureLayer(  featureTable: ServiceFeatureTable(  url: .alaskaParksFeatureServer.appending(component: "1")  )  )   /// The text describing the loading status of the sample.  @State private var loadingStatusText: String?   /// The point on the screen where the user tapped.  @State private var tapPoint: CGPoint?   /// The results from querying related features.  @State private var queryResults: RelatedFeatureQueryResults?   /// The error shown in the error alert.  @State private var error: Error?   var body: some View {  MapViewReader { mapViewProxy in  MapView(map: map)  .onSingleTapGesture { screenPoint, _ in  tapPoint = screenPoint  }  .task(id: tapPoint) {  guard let tapPoint else { return }  parksFeatureLayer.clearSelection()   do {  // Identifies the tapped screen point.  loadingStatusText = "Identifying feature…"  defer { loadingStatusText = nil }   let identifyResult = try await mapViewProxy.identify(  on: parksFeatureLayer,  screenPoint: tapPoint,  tolerance: 12  )   // Selects the first feature in the result.  guard let parkFeature = identifyResult.geoElements.first  as? ArcGISFeature else { return }  parksFeatureLayer.selectFeature(parkFeature)   // Queries for related features of the identified feature.  loadingStatusText = "Querying related features…"   let parksFeatureTable = parksFeatureLayer.featureTable as! ServiceFeatureTable  let queryResults = try await parksFeatureTable.queryRelatedFeatures(  to: parkFeature  )  self.queryResults = RelatedFeatureQueryResults(results: queryResults)  } catch {  self.error = error  }  }  .sheet(  item: $queryResults,  onDismiss: parksFeatureLayer.clearSelection  ) { newQueryResults in  RelatedFeaturesList(results: newQueryResults.results)  .frame(idealWidth: 320, idealHeight: 380)  }  .overlay(alignment: .center) {  // Shows a progress view when there is a loading status.  if let loadingStatusText {  VStack {  Text(loadingStatusText)  ProgressView()  }  .padding()  .background(.ultraThickMaterial)  .clipShape(.rect(cornerRadius: 10))  .shadow(radius: 50)  }  }  .task {  // Loads the parks feature layer and zooms the viewpoint to its extent.  do {  loadingStatusText = "Loading feature layer…"  defer { loadingStatusText = nil }   try await parksFeatureLayer.load()  map.addOperationalLayer(parksFeatureLayer)   guard let parksLayerExtent = parksFeatureLayer.fullExtent else { return }  await mapViewProxy.setViewpointGeometry(parksLayerExtent, padding: 20)  } catch {  self.error = error  }  }  .errorAlert(presentingError: $error)  }  } }  private extension QueryRelatedFeaturesView {  /// A list of features from given related feature query results.  struct RelatedFeaturesList: View {  /// The results to display in the list.  let results: [RelatedFeatureQueryResult]   /// The action to dismiss the view.  @Environment(\.dismiss) private var dismiss   var body: some View {  NavigationStack {  List {  ForEach(Array(results.enumerated()), id: \.offset) { offset, result in  let relatedTableName = result.relatedTable?.tableName  Section(relatedTableName ?? "Feature Table \(offset + 1)") {  ForEach(result.featureDisplayNames, id: \.self) { featureName in  Text(featureName)  }  }  }  }  .navigationTitle(  results.first?.feature?.attributes["UNIT_NAME"] as? String ?? "Origin Feature"  )  .navigationBarTitleDisplayMode(.inline)  .toolbar {  ToolbarItem(placement: .confirmationAction) {  Button("Done") { dismiss() }  }  }  }  }  }   /// A struct containing the results from a related features query.  struct RelatedFeatureQueryResults: Identifiable {  /// A universally unique id to identify the type.  let id = UUID()  /// The related feature query results.  let results: [RelatedFeatureQueryResult]  } }  private extension RelatedFeatureQueryResult {  /// The display names of the result's features.  var featureDisplayNames: [String] {  guard let displayFieldName = relatedTable?.layerInfo?.displayFieldName else { return [] }  return features()  .compactMap { $0.attributes[displayFieldName] as? String }  .sorted()  } }  private extension URL {  /// The URL to the "Alaska National Parks Preserves Species" feature server.  static var alaskaParksFeatureServer: URL {  URL(string: "https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer")!  } }  #Preview {  QueryRelatedFeaturesView() }

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