Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 41 additions & 17 deletions OpenGpxTracker/GPXFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,38 +74,44 @@ class GPXFileManager: NSObject {
/// - Parameters:
/// - fileURL: destination URL, basically it is file path.
/// - gpxContents: String with the contents to be saved. The XML contents of the GPX file
/// - Returns: `nil` on success, or the `Error` that caused the write to fail.
///
class func saveToURL(_ fileURL: URL, gpxContents: String) {
@discardableResult
class func saveToURL(_ fileURL: URL, gpxContents: String) -> Error? {
// Save file
print("Saving file at path: \(fileURL)")
// When the GPX files folder is a user-picked location (f.i. an iCloud
// Drive folder), the folder URL is security-scoped and must be unlocked
// before we can write into it. This mirrors what `fileList` does for reads.
let folderURL = GPXFileManager.GPXFilesFolderURL
let secured = folderURL.startAccessingSecurityScopedResource()
defer {
if secured {
folderURL.stopAccessingSecurityScopedResource()
}
}
// write gpx to file
var writeError: NSError?
let saved: Bool
do {
try gpxContents.write(toFile: fileURL.path, atomically: true, encoding: String.Encoding.utf8)
saved = true
} catch let error as NSError {
writeError = error
saved = false
}
if !saved {
if let error = writeError {
print("[ERROR] GPXFileManager:save: \(error.localizedDescription)")
}
return nil
} catch {
print("[ERROR] GPXFileManager:save: \(error.localizedDescription)")
return error
}

}
///
/// Saves in the default folder the filename with the gpxContents
///
/// - Parameters:
/// - filename: gpx filename with .gpx extension (f.i: `hola.gpx`) or without extension (f.i: `hola`)
/// - gpxContents: String with the contents to be saved. The XML contents of the GPX file
/// - Returns: `nil` on success, or the `Error` that caused the write to fail.
///
class func save(_ filename: String, gpxContents: String) {
@discardableResult
class func save(_ filename: String, gpxContents: String) -> Error? {
// Check if name exists
let fileURL: URL = self.URLForFilename(filename)
GPXFileManager.saveToURL(fileURL, gpxContents: gpxContents)
return GPXFileManager.saveToURL(fileURL, gpxContents: gpxContents)
}

///
Expand All @@ -123,12 +129,21 @@ class GPXFileManager: NSObject {
return
}

// The destination folder may be a security-scoped user-picked location
// (f.i. an iCloud Drive folder), so unlock it before moving into it.
let folderURL = GPXFilesFolderURL
let secured = folderURL.startAccessingSecurityScopedResource()
defer {
if secured {
folderURL.stopAccessingSecurityScopedResource()
}
}
// attempt to move file
do {
let url = GPXFilesFolderURL.path + "/" + fileName
let url = folderURL.path + "/" + fileName
try FileManager().moveItem(atPath: fileURL.path, toPath: url)
}

// file move failure
catch {
print("GPXFileManager:: save failed, error: \(error)")
Expand All @@ -138,6 +153,15 @@ class GPXFileManager: NSObject {
/// Removes a file on the specified URL
class func removeFileFromURL(_ fileURL: URL) {
print("Removing file at path: \(fileURL)")
// The file may live in a security-scoped user-picked folder (f.i. an
// iCloud Drive folder), so unlock it before attempting to delete.
let folderURL = GPXFileManager.GPXFilesFolderURL
let secured = folderURL.startAccessingSecurityScopedResource()
defer {
if secured {
folderURL.stopAccessingSecurityScopedResource()
}
}
let defaultManager = FileManager.default
var error: NSError?
let deleted: Bool
Expand Down
28 changes: 25 additions & 3 deletions OpenGpxTracker/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,12 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
print("Save File \(String(describing: filename))")
// Export to a file
let gpxString = self.map.exportToGPXString()
GPXFileManager.save(filename!, gpxContents: gpxString)
if let error = GPXFileManager.save(filename!, gpxContents: gpxString) {
// The write failed (f.i. a stale/denied security-scoped iCloud
// folder). Tell the user instead of silently pretending it saved.
self.displaySaveErrorAlert(error)
return
}
self.lastGpxFilename = filename!
self.map.coreDataHelper.coreDataDeleteAll(of: CDRoot.self)
self.map.coreDataHelper.clearAllExceptWaypoints()
Expand All @@ -1213,9 +1218,26 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
alertController.addAction(cancelAction)

present(alertController, animated: true)

}


///
/// Displays an alert informing the user that saving the GPX file failed.
///
/// This is most commonly hit when the GPX files folder is a user-picked
/// location (f.i. an iCloud Drive folder) whose security-scoped access could
/// not be acquired — e.g. the bookmark went stale or the folder was moved.
///
/// - Parameter error: The underlying error returned by the file write.
///
func displaySaveErrorAlert(_ error: Error) {
let alertController = UIAlertController(title: NSLocalizedString("SAVE_ERROR_TITLE", comment: "no comment"),
message: error.localizedDescription,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("DONE", comment: "no comment"), style: .default))
present(alertController, animated: true)
}

///
/// There was a memory warning. Right now, it does nothing but to log a line.
///
Expand Down
1 change: 1 addition & 0 deletions OpenGpxTracker/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@
"DEFAULT_NAME_SAMPLE_OUTPUT_TITLE" = "Sample: ";
"DEFAULT_NAME_USE_UTC" = "Use UTC Time?";
"DEFAULT_NAME_ENGLISH_LOCALE" = "Force English language based date formatting?";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,4 @@
"LOCATION" = "Location";
"LOCATION_TIME" = "Location's Time";

"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,4 @@
"GMT_FULL" = "GMT completo";
"LOCATION" = "Ubicación";
"LOCATION_TIME" = "Hora en la ubicación";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/fi-FI.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ Created by Phiilman on 28.03.20.

"CONTINUE_SESSION" = "Jatka tapahtumaa";
"SAVE_START_NEW" = "Tallenna ja aloita uusi tapahtuma";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,4 @@
"LOCATION" = "Localisation";
"LOCATION_TIME" = "Heure locale";

"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/it.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@
"DEFAULT_NAME_SAMPLE_OUTPUT_TITLE" = "Esempio: ";
"DEFAULT_NAME_USE_UTC" = "Usare fuso orario UTC?";
"DEFAULT_NAME_ENGLISH_LOCALE" = "Forza la formattazione della data basata sulla lingua inglese?";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,4 @@
"GMT_FULL" = "GMT完全";
"LOCATION" = "地域";
"LOCATION_TIME" = "地域の時刻";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/nl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,4 @@
"GMT_FULL" = "GMT volledig";
"LOCATION" = "Locatie";
"LOCATION_TIME" = "Locatietijd";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/pt-BR.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,4 @@
"LOCATION" = "Localização";
"LOCATION_TIME" = "Hora da localização";

"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,4 @@
"LOCATION" = "Местоположение";
"LOCATION_TIME" = "Время местоположения";

"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,4 @@
"DEFAULT_NAME_SAMPLE_OUTPUT_TITLE" = "Sample: ";
"DEFAULT_NAME_USE_UTC" = "Use UTC Time?";
"DEFAULT_NAME_ENGLISH_LOCALE" = "Force English language based date formatting?";
"SAVE_ERROR_TITLE" = "Could not save GPX file";
1 change: 1 addition & 0 deletions OpenGpxTracker/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,4 @@
"GMT_FULL" = "GMT 整体";
"LOCATION" = "地点";
"LOCATION_TIME" = "地点时区";
"SAVE_ERROR_TITLE" = "Could not save GPX file";