extension String { func htmlEscaped() -> String { replacingOccurrences(of: "&", with: "&") .replacingOccurrences(of: "\"", with: """) .replacingOccurrences(of: "'", with: "'") .replacingOccurrences(of: "<", with: "<") .replacingOccurrences(of: ">", with: ">") } var nonEmpty: String? { isEmpty ? nil : self } } extension String { var fileExtension: String? { let parts = components(separatedBy: ".") guard parts.count > 1 else { return nil } return parts.last } var fileNameAndExtension: (fileName: String, fileExtension: String?) { let parts = components(separatedBy: ".") guard parts.count > 1 else { return (self, nil) } return (fileName: parts.dropLast().joined(separator: "."), fileExtension: parts.last) } }