My app allows the user to create a point feature and attach multiple photos to it. The photos are displayed in a custom callout which appears when the user taps the feature. The point is created and the attachments are added to it successfully; I know because I can see the photos when I select the point on the map and no client-side errors are thrown. However, the attachments are gone the next time I open the app. The point appears and I can see it in ArcGIS Pro on the back end, but there are no attachments in the associated table.
This error is logged in the Server Manager: "Unable to complete upload operation, File size or type not supported for this service". The photos are less than 2 MB each and I have never uploaded more than 3 at a time.
I know attachments are enabled for the layer because I successfully added this feature with a photo via ArcGIS Pro and it saved to the database. This leads me to believe the failure is either on the front end or perhaps a setting the attachment is violating, but I don't know how to check that.
Here are the steps to create the feature and any photo attachments:
1. The user presses a button which transitions to a new screen where they select photos.
2. The user selects any number of photos from the gallery or takes new ones with the camera. This is done using UIImagePickerController.
3. The user presses "done". The app prepares to transition to a view controller where the point attributes are set. The point is created in the "prepare" function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "create-photo-point-segue" {
let nc = segue.destination as! NewFeatureNavigationController
let vc = nc.topViewController as! CreateFeatureViewController
let template: AGSFeatureTemplate? = (photoFeatureLayer?.featureTable as? AGSServiceFeatureTable)?.featureTemplates[0]
vc.template = template
let newAgsFeature = (photoFeatureLayer?.featureTable as? AGSServiceFeatureTable)?.createFeature(with: template!)
newAgsFeature!.attributes.setValue(primaryLocation?.latitude, forKey: "LATITUDE")
newAgsFeature!.attributes.setValue(primaryLocation?.longitude, forKey: "LONGITUDE")
newAgsFeature!.attributes.setValue(primaryDate, forKey: "TIMESTAMP")
let point = AGSPoint(x: primaryLocation!.longitude , y: primaryLocation!.latitude, spatialReference: .wgs84())
newAgsFeature!.geometry = point
attachTxtDocs(feature: newAgsFeature!, photoArray: photoSelectionData)
vc.feature = newAgsFeature
vc.featureAttachments = photoSelectionData
vc.hasAttachments = true
vc.layer = photoFeatureLayer!
nc.collectionPoint = (self.navigationController as! AddPhotoFeatureNavigationController).collectionPoint
let mapVC = (self.navigationController as! AddPhotoFeatureNavigationController).mapVC
mapVC?.navController = nc
nc.mapVC = mapVC
nc.intermediateVC = self
}
}
3a. The photos are serialized to txt documents (necessary to capture metadata) and attached to the newly-created point. I'm aware that because the attachment is a text document that could be why it doesn't save, but I doubt it because I have an alternate function which does not serialize the photo and it experiences the same problem.
func attachTxtDocs(feature:AGSArcGISFeature, photoArray:[[UIImagePickerController.InfoKey:Any]]) {
var i = 0
var tempFileUrls: [URL] = []
DispatchQueue.main.async {
SVProgressHUD.show(withStatus: "Attaching Photos...")
}
for photo in photoArray {
var type: String
if let typeOptional = (photo[UIImagePickerController.InfoKey.imageURL] as! URL?) {
let urlTokens = typeOptional.absoluteString.split(separator: ".")
type = urlTokens[urlTokens.count - 1].description
}
else {
type = "jpg"
}
var imageData: Data?
switch type {
case "jpg", "jpeg":
imageData = (photo[UIImagePickerController.InfoKey.originalImage] as! UIImage).jpegData(compressionQuality: 0.5)!
case "png":
imageData = (photo[UIImagePickerController.InfoKey.originalImage] as! UIImage).pngData()!
default:
print("Invalid type")
}
guard imageData != nil else {
SVProgressHUD.dismiss()
return
}
let fileName = serializePhoto(lat: String(geolocations[i].latitude),
lon: String(geolocations[i].longitude),
timestamp: timestamps[i].description,
fileType: type,
photoData: imageData!)
let fileUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName).appendingPathExtension("txt")
tempFileUrls.append(fileUrl)
do {
let photoData = try Data(contentsOf: fileUrl)
feature.addAttachment(withName: "attachment" + String(i),
contentType: "txt",
data: photoData) { (attachment, error) in
SVProgressHUD.dismiss()
if let error = error {
print(error.localizedDescription)
}
if attachment != nil {
print("Photo successfully attached.")
self.removeTempDoc(fileUrl: fileUrl)
}
}
}
catch {
print("Failed to fetch photo data from " + fileUrl.absoluteString)
return
}
i += 1
}
}
4. The feature's attributes are set and is loaded after the transition to the final view.
5. The user confirms the attributes are correct and hits "save". The edits are applied to the table.
func applyEdits(to table:AGSServiceFeatureTable) {
SVProgressHUD.setDefaultMaskType(.black)
DispatchQueue.main.async {
SVProgressHUD.show(withStatus: "Saving")
}
table.applyEdits(completion: { (results, error) in
SVProgressHUD.dismiss()
if let error = error {
print(error)
let alert = UIAlertController(title: "Alert!", message: "Unable to create feature: \(error.localizedDescription)", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
return
}
if self.referenceFeature != nil {
self.deleteReferenceFeature()
} else {
self.navigationController?.dismiss(animated: true, completion: nil)
}
})
}
This app uses ArcGIS Runtime v100.6. The back end is ArcServer 10.7.1.
Can someone offer a solution?