I am developing a Map Application having various GIS functionalities on different ViewControllers connected via Navigation Controller. Each ViewController having AGSMapView loading some layers on map. While opening and closing (going back to main menu) first map view controller, it is working fine. Switching over to the next ViewController from main menu, it is also working fine. But after closing the second view controller and opening again the first view controller, it gives fatal error as mentioned in subject. I am enclosing the complete app code as Zip file, screenshot showing error and GIF file of Simulator Screen Recording.
It would be very much appreciated if anybody can help me to resolve this issue.
Thanks in advance
Afroz Alam
Solved! Go to Solution.
Declaring map as optional means that it can be nil at some point. Your crash was because by using "!" you had said "I know it will never be nil when I use it", but it turned out it was nil.
If you declare it as optional with "?", then Swift will check if the map is nil. You won't get a crash, but your code might be skipped. And you must include logic to acknowledge that it could be nil when you're using it. One common approach is optional binding using an "if let" block.
So your code above could be included in an "if let" block:
func Load_Municipality_Boundary() {
if let map = map {
self.munFeatureTable = AGSServiceFeatureTable(url: munCensus)
let munFeatureLayer = AGSFeatureLayer(featureTable: self.munFeatureTable)
map.operationalLayers.add(munFeatureLayer)
let censusFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services.gisqatar.org.qa/server/rest/services/Vector/Census_Municipality/FeatureServer/1")!)
map.tables.addObjects(from: [censusFeatureTable])
mapView.selectionProperties.color = .yellow
self.munFeatureLayer = munFeatureLayer
} else {
print("Map is not set!!")
}
}
That "if {}" block will only execute if map is not nil. It's still up to you to make sure the "map" variable is set to an AGSMap instance, or you will see a "Map is not set!!" message written to the console.
Optional variables are a fundamental Swift pattern, and not something specific to the ArcGIS Runtime SDK for iOS. I suggest you familiarize yourself with how optional variables are used in Swift. There are many tutorials and articles available online, such as this one.
Ultimately though, you need to work out why your app is setting "map" to nil at some point, and make sure that you set the "map" variable to a new AGSMap again before you use it in the above code.
Beware the exclamation mark. This code is dangerous:
var map: AGSMap!
Declaring map as an implicitly unwrapped optional is stating that even though the variable is optional and can be nil, that you KNOW that whenever you access it, it will not be nil. Unfortunately, in your app, that's not the case.
When you click "Maps" a second time and SetMapExtent() is called, the map variable above is nil. Make sure that it isn't and your code will get a bit further. But I would recommend not declaring it as an implicitly unwrapped optional in the first place.
func SetMapExtent () {
let envelope = AGSEnvelope (xMin:216985.909, yMin:375531.776, xMax:241940.565, yMax:415497.915, spatialReference: self.mapView.spatialReference)
self.map.initialViewpoint = AGSViewpoint(targetExtent: envelope)
self.mapView.map = map
}
However, self.mapView.spatialReference is also nil, so the envelope you create will have a nil spatial reference. That will also probably cause problems.
Dear Nicholas,
Thank you very much for your reply. I have tried by declaring with question mark i.e. var map : AGSMap?, even it is not working. If it is not declared with implicitly unwrapped optional the how can add some operational layers later like in the code below;
func Load_Municipality_Boundary() {
self.munFeatureTable = AGSServiceFeatureTable(url: munCensus)
let munFeatureLayer = AGSFeatureLayer(featureTable: self.munFeatureTable)
map.operationalLayers.add(munFeatureLayer)
let censusFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services.gisqatar.org.qa/server/rest/services/Vector/Census_Municipality/FeatureServer/1")!)
map.tables.addObjects(from: [censusFeatureTable])
mapView.selectionProperties.color = .yellow
self.munFeatureLayer = munFeatureLayer
}
Would you please suggest me the best way to declare map so that it never happens and app does not crash.
Warm regards,
Afroz
Declaring map as optional means that it can be nil at some point. Your crash was because by using "!" you had said "I know it will never be nil when I use it", but it turned out it was nil.
If you declare it as optional with "?", then Swift will check if the map is nil. You won't get a crash, but your code might be skipped. And you must include logic to acknowledge that it could be nil when you're using it. One common approach is optional binding using an "if let" block.
So your code above could be included in an "if let" block:
func Load_Municipality_Boundary() {
if let map = map {
self.munFeatureTable = AGSServiceFeatureTable(url: munCensus)
let munFeatureLayer = AGSFeatureLayer(featureTable: self.munFeatureTable)
map.operationalLayers.add(munFeatureLayer)
let censusFeatureTable = AGSServiceFeatureTable(url: URL(string: "https://services.gisqatar.org.qa/server/rest/services/Vector/Census_Municipality/FeatureServer/1")!)
map.tables.addObjects(from: [censusFeatureTable])
mapView.selectionProperties.color = .yellow
self.munFeatureLayer = munFeatureLayer
} else {
print("Map is not set!!")
}
}
That "if {}" block will only execute if map is not nil. It's still up to you to make sure the "map" variable is set to an AGSMap instance, or you will see a "Map is not set!!" message written to the console.
Optional variables are a fundamental Swift pattern, and not something specific to the ArcGIS Runtime SDK for iOS. I suggest you familiarize yourself with how optional variables are used in Swift. There are many tutorials and articles available online, such as this one.
Ultimately though, you need to work out why your app is setting "map" to nil at some point, and make sure that you set the "map" variable to a new AGSMap again before you use it in the above code.