2015-04-26 5 views
1

Я пытаюсь интегрировать представление закладок Pinboard (путем разбора RSS-канала и отображения его в UITableView) в моем приложении-браузере, и я сталкиваюсь с несколькими проблемами. Вы можете увидеть мои предыдущие вопросы here и here. Я пытаюсь использовать переменные, которые я собрал у своего пользователя в Alert, чтобы получить секретный токен RSS-канала пользователя. Однако, когда я запускаю приложение, это вызывает сбой с ошибкой «EXC_BAD_INSTRUCTION» со следующей консоли вывода:Как разобрать RSS-канал, используя NSXMLParser с переменными в адресе канала, нажав кнопку в предупреждении UIAlertController в Swift?

fatal error: unexpectedly found nil while unwrapping an Optional value

Я проверил все, но я не могу найти nil.

Это код, я использую:

class SettingsTableViewController: UITableViewController, MFMailComposeViewControllerDelegate, NSXMLParserDelegate { 

var _pinboardUsername: String? 
var _pinboardAPIToken: String? 

var parser: NSXMLParser = NSXMLParser() 
var bookmarks: [Bookmark] = [] 
var pinboardSecret: String = String() 
var eName: String = String() 

... 

    @IBAction func pinboardUserDetailsRequestAlert(sender: AnyObject) { 
    //Create the AlertController 
    var pinboardUsernameField :UITextField? 
    var pinboardAPITokenField :UITextField? 
    let pinboardUserDetailsSheetController: UIAlertController = UIAlertController(title: "Pinboard Details", message: "Please enter your Pinboard Username and API Token to access your bookmarks", preferredStyle: .Alert) 
    //Add a text field 
    pinboardUserDetailsSheetController.addTextFieldWithConfigurationHandler({(usernameField: UITextField!) in 
     usernameField.placeholder = "Username" 
     usernameField.text = self._pinboardUsername 
     var parent = self.presentingViewController as! ViewController 
     pinboardUsernameField = usernameField 

    }) 
    pinboardUserDetailsSheetController.addTextFieldWithConfigurationHandler({(apiTokenField: UITextField!) in 
     apiTokenField.placeholder = "API Token" 
     apiTokenField.text = self._pinboardAPIToken 
     var parent = self.presentingViewController as! ViewController 
     pinboardAPITokenField = apiTokenField 

    }) 
    pinboardUserDetailsSheetController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)) 
    pinboardUserDetailsSheetController.addAction(UIAlertAction(title: "Done", style: .Default, handler: { (action) -> Void in 
     // Now do whatever you want with inputTextField (remember to unwrap the optional) 
     var valueUser = pinboardUsernameField?.text 
     NSUserDefaults.standardUserDefaults().setValue(valueUser, forKey: AppDefaultKeys.PinboardUsername.rawValue) 
     var valueAPI = pinboardAPITokenField?.text 
     NSUserDefaults.standardUserDefaults().setValue(valueAPI, forKey: AppDefaultKeys.PinboardAPIToken.rawValue) 
     let url:NSURL = NSURL(string: "https://api.pinboard.in/v1/user/secret/?auth_token=\(valueAPI)")! 
     self.parser = NSXMLParser(contentsOfURL: url)! 
     self.parser.delegate = self 
     self.parser.parse() 

     func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) { 
      self.eName = elementName 
      if elementName == "result" { 
       self.pinboardSecret = String() 
       NSUserDefaults.standardUserDefaults().setValue(self.pinboardSecret, forKey: AppDefaultKeys.PinboardSecret.rawValue) 
      } 
     } 
    })) 
    self.presentViewController(pinboardUserDetailsSheetController, animated: true, completion: nil) 
} 

Ошибка в let url: NSURL = ... строке функции синтаксического анализатора.

Что я делаю неправильно?

ответ

2

Вопрос в том, что valueAPI является факультативным. Если, например, valueAPI был foo, ваша строка URL будет выглядеть так:

https://api.pinboard.in/v1/user/secret/?auth_token=Optional("foo") 

Я предлагаю строить свою строку URL-первых, глядя на него, и вы увидите, что я имею в виду.

Подводя итог, ссылка Optional("...") в URL-адресе недействительна и, таким образом, создание URL-адреса с этой строкой не удастся. И используя !, чтобы развернуть, что необязательно NSURL будет разбиваться, как вы начертили.

Перед тем, как использовать его в строке URL, необходимо развернуть valueAPI.

let url:NSURL = NSURL(string: "https://api.pinboard.in/v1/user/secret/?auth_token=\(valueAPI!)")! 

Или вы могли бы сделать valueAPI неявно развернутый необязательными.

Честно говоря, если есть любой шанс, что valueAPI может быть nil, я предпочел бы использовать необязательные связывания (т.е. if let положения), чтобы вы могли грациозно обнаружить эти проблемы в будущем.


Что касается совершенно отдельных задач XML разбора, есть несколько вопросов:

  1. Я предлагаю получение ответа асинхронно с помощью NSURLSession. Использование NSXMLParser с contentsOfURL выполняет запрос синхронно, что приводит к плохому UX, и процесс сторожевого таймера iOS может убить ваше приложение.

    Вместо этого используйте NSURLSession метод dataTaskWithURL, а затем используйте полученный результат NSData с объектом NSXMLParser.

  2. Если вы это сделаете, вы также можете преобразовать объект ответа NSData в строку и изучить ее. Часто, если есть проблемы с API, ответ будет содержать некоторое описание характера ошибки (иногда это текст, иногда это HTML, иногда это XML, зависит от того, как был разработан этот веб-сервис). Если вы не получите ответ XML, который вы ожидали, вам действительно нужно посмотреть на исходный ответ, чтобы определить, что не так.

  3. Ваш NSXMLParserDelegate метод didStartElement определяется внутриaddAction блок. Это должен быть метод самого класса, не зарытый внутри этого закрытия.

  4. Ваш didStartElement хранит секрет. Но у вас еще нет секретов, поэтому нет смысла пытаться его сохранить.

  5. Я не вижу других NSXMLParserDelegate методов (foundCharacters, didEndElement и parseErrorOccurred, по крайней мере). У вас есть те, которые определены в других местах? См https://stackoverflow.com/a/27104883/1271826

+0

Хорошая точка! :-) – zisoft

+0

Привет @Rob, это действительно хороший момент. Однако, даже когда я развожу опцию с помощью! он все еще не работает. Я использую API-интерфейс Pinboard, как указано [здесь] (https://pinboard.in/api), который должен только возвращать секрет в имени элемента результата: '$ curl https://api.pinboard.in/v1 ?/пользователь/тайна/auth_token = пользователь: ЗНАК 6493a84f72d86e7de130 ', Как я могу быть уверен, что он получил это правильно? Когда я переключаю переменную с самим токеном, он работает с curl, но он не будет работать с Xcode и кодом. – PastaCoder

+0

В дополнение к моему предыдущему комментарию, я думаю, что это что-то с eName (поскольку оно работает с curl на терминале, а не в коде), но я не уверен, что ... – PastaCoder

1
let url:NSURL = NSURL(string: "https://api.pinboard.in/v1/user/secret/?auth_token=\(valueAPI)")! 

NSURL() является failable инициализатор, то есть он может потерпеть неудачу при инициализации (неверный формат URL, сервер не отвечает, ...).

Выбирая развертку с помощью !, вы заявляете, что абсолютно уверены, что получите действительный объект, но вы этого не сделаете. Это приводит к фатальной ошибке.

+0

Привет Zisoft, Спасибо за ответ, в то время как приложение не врезаться в настоящее время, я не получаю мой pinboardSecret заполнен, что весь смысл синтаксического анализатора. – PastaCoder

 Смежные вопросы

  • Нет связанных вопросов^_^