Создание NSURL из NSString и NSUserDefaults, сломанных с Xcode 5 и Mavericks

В нашей компании мы используем приложение для Mac, которое отлично работало в OS X Mountain Lion. После обновления до OS X Mavericks он перестал работать. Поскольку мы еще не обновили все, мы ясно видим, что на машинах ML это все еще работает, а на машинах Mavericks - нет.

Погрузившись в код и запустив его из Xcode 5, быстро стало ясно, что следующий фрагмент кода, отвечающий за создание вызова API к Trello, ведет себя не так, как ожидалось:

NSString *token = [[NSUserDefaults standardUserDefaults] objectForKey:TRELLOTOKEN];    

NSLog(@"TOKEN: %@", token);    

NSString *urlString = [NSString stringWithFormat:@"/1/cards/%@/idList?value=%@&key=%@&token=%@", taskID, DOINGLISTID, TRELLOKEY, token];    

NSLog(@"URLSTRING: %@", urlString); 

NSURL *url = [NSURL URLWithString:urlString relativeToURL:[NSURL URLWithString:TRELLOLOGENDPOINT]];  

NSLog(@"URL: %@", url);

Вывод:

TOKEN: thetokenstring
URLSTRING: /1/cards/.../idList?value=...&key=...&token=thetokenstring
URL: (null)

Я изменил переменные taskID, DOINGLISTID, TRELLOKEY на ..., чтобы они были короткими и анонимными. Я могу заверить, что ключи не содержат символов, которые необходимо экранировать (они также должны были бы экранироваться в ML).

Проблема в том, что моя переменная url остается нулевой после назначения с помощью функции NSURL URLWithString.

Есть способ исправить эту ситуацию, просто присвоив строку переменной NSString *token. Итак, если мы заменим первую строку на:

NSString *token = @"thetokenstring";

Тогда это работает. Вы можете просто скопировать и вставить токен из вывода NSLog, если хотите.

Вывод:

TOKEN: thetokenstring
URLSTRING: /1/cards/.../idList?value=...&key=...&token=thetokenstring
URL: /1/cards/.../idList?value=...&key=...&token=thetokenstring -- https://trello.com

Очевидно, мне нужен мой токен из моего NSUserDefaults.

Может ли кто-нибудь пролить свет на это? Я хотел бы знать, почему это не работает (больше) и как я могу это исправить.

Я уже пробовал следующее:

  • Явное приведение результата NSUserDefaults к NSString.
  • Инициализация токена NSString * с помощью:

    [NSString строкаWithString:...]

К сожалению, не повезло...


person S. Kuijper    schedule 13.11.2013    source источник
comment
Это немного странное предложение, но попробуйте NSLog(@"%@", [NSArray arrayWithObject:token]);. В описаниях массивов используется старый формат списка ASCII, который будет подсвечиваться, если token содержит что-то, чего вы не ожидаете. Думаю, я бы также зарегистрировал [token class], пока я этим занимаюсь.   -  person Ken Thomases    schedule 14.11.2013


Ответы (2)


Хорошо, я нашел решение. На основе отличного комментария Кена Томаса.

Оказалось, что токен содержит символ новой строки. Звучит довольно глупо, а? Это может произойти, потому что мне нужно отфильтровать код из DOM очень простой страницы после того, как пользователь авторизует приложение.

Я полагаю, что реализация NSURL немного изменилась, поэтому она не позволяет использовать символ новой строки в конце строки URL-адреса (и не должен). Однако я бы не исключал существования каких-либо других возможных объяснений.

person S. Kuijper    schedule 14.11.2013
comment
Я был бы очень удивлен, если бы реализация NSURL изменилась таким образом. В примечаниях к выпуску упоминается, что раньше он не проверял должным образом односимвольные строки, но ничего кроме этого - person Mike Abdullah; 11.01.2014
comment
Что ж, кажется, у меня нет другого выбора, кроме как быть очень удивленным. Мое тестирование показало, что до версии 10.9 +URLWithString: и его друзья с радостью позволяли символу last быть чем угодно, кодируя его в процентах, если это необходимо. Недопустимые символы в любой другой позиции приведут к возврату nil. Я подозреваю, что примечание к выпуску просто недостаточно широкое - person Mike Abdullah; 11.01.2014

код выглядит нормально!
в любом случае, вы можете заменить первую строку этой, чтобы увидеть, работает она или нет:

NSString *token = [[[NSUserDefaults standardUserDefaults] objectForKey:TRELLOTOKEN] description];

это просто мысли, может быть так или нет! я думаю, что метод, который вы использовали, возвращает (id), что нормально для передачи его в NSString, он должен работать так же, как он работает в выводе сообщения NSLog (и предыдущих операционных системах). метод описания просто возвращает (NSString *), который является тем же типом объекта токена. (id) является динамической типизацией и может вызвать некоторые проблемы. Хорошо писать код так, как вы говорите, что хотите вернуть именно NSString!
кстати, [[NSUserDefaults standardUserDefaults] objectForKey:TRELLOTOKEN] может вернуть nil (в зависимости от логики вашего кода!). поэтому лучше всего использовать его следующим образом:

NSString *token;
NSObject *tokenObj = [[NSUserDefaults standardUserDefaults] objectForKey:TRELLOTOKEN];
if (token != nil) {
    token = [tokenObj description];
} else {
    token = @"";
}
NSString *urlString = [NSString stringWithFormat:@"/1/cards/%@/idList?value=%@&key=%@&token=%@", taskID, DOINGLISTID, TRELLOKEY, token];    
NSURL *url = [NSURL URLWithString:urlString relativeToURL:[NSURL URLWithString:TRELLOLOGENDPOINT]];
NSLog(@"URL: %@", url);

надеюсь решить вашу дилемму! ;)

person Shamim    schedule 13.11.2013
comment
Зачем заменять первую строку на это? Пожалуйста, укажите причину, не просто говорите, сделайте это и не объясняйте, почему -1 на основании этого я удалю, если оно будет улучшено. - person Popeye; 13.11.2013
comment
метод, который вы использовали, возвращает (id), который можно передать в NSString, он должен работать так же, как и в выходном сообщении NSLog (и в предыдущих операционных системах). метод описания просто возвращает (NSString *), который является тем же типом объекта токена. (id) - это динамическая типизация, и у меня возникают проблемы. Это просто мысль! вывод, который я сделал, может быть не в этом случае! - person Shamim; 13.11.2013
comment
Пожалуйста, обновите свой ответ с этим. Что делать, если возвращается что-то, что не имеет метода description? Что произойдет, если nil будет возвращено, поскольку nil не имеет description, код вылетит на этой строке. - person Popeye; 13.11.2013
comment
любой объект в мире Objectiv-C имеет метод описания. Кстати, вы можете переопределить метод описания, чтобы вернуть подходящую для вас строку NSString. когда вы (в своем коде) назначаете вывод объекту NSString (токену), вы фактически помещаете то, что возвращает метод описания. но здесь вы говорите, что я хочу, чтобы тип вывода был именно NSString, а не (id)! Вы можете хотя бы попробовать это и дать отзыв, что проблема решается или остается прежней? - person Shamim; 13.11.2013
comment
Дело не во мне. Я сомневаюсь в вашем коде, потому что если [[NSUserDefaults standardUserDefaults] objectForKey:TRELLOTOKEN] вернет nil, эта строка выйдет из строя. nil не является объектом, поэтому nil это будет означать, что этому объекту ничего не было присвоено, поэтому, когда вы запускаете description на nil, это похоже на выполнение [nil description];, и это просто сломает мир. Поэтому, хотя на первый взгляд может показаться, что это работает, по этой причине это неверно, поэтому ваш код неверен и создаст больше ошибок, чем исправление. - person Popeye; 13.11.2013
comment
Ааа! получил вашу точку зрения! да, вы правы, вывод может быть нулевым. чтобы вы могли получить вывод как объект (id), чтобы увидеть, равен ли он нулю или нет. затем вызовите метод описания! Кстати, я нигде не говорил, что ваш код неправильный, а мой правильный, и вы должны использовать его именно так! делай как хочешь! - person Shamim; 13.11.2013
comment
Кстати! вы можете вызвать любой метод, который вам нравится, для объекта nil! просто так ничего не будет! NSObject * theNullObject = nil; [theNullObject description]; - person Shamim; 13.11.2013
comment
Вы похоже не читаете мои комментарии. Я не человек, задающий вопрос (тот, у кого есть проблема). Я случайный разработчик, который ставит под сомнение ваш ответ, чтобы улучшить его. Просто сказать, что использовать это не очень хороший ответ, вам нужно отредактировать свой ответ, чтобы объяснить себя. Честно говоря, мне все равно, возвращает ли это nil или нет, я пытаюсь заставить вас улучшить фактический ответ, а не комментировать его. - person Popeye; 13.11.2013
comment
:D Я сожалею об этом. Я думал, что поругался с вопрошающим! :D Я сделаю это. Спасибо. :) - person Shamim; 13.11.2013
comment
Помимо нулевого обсуждения, я просто попытался использовать явное описание. К сожалению, это не помогает. Тот же результат. - person S. Kuijper; 13.11.2013
comment
Как и было обещано, ответ был улучшен, поэтому я удалил свой -1 - person Popeye; 14.11.2013