iOS: передача пользовательских NSURL в NSURLProtocol

Мне нужно передать дополнительную информацию вместе с UIWebView loadRequest: так что доходит до моей реализации NSURLProtocol, Информация не может быть связана с NSURLRequest потому что информация должна быть сохранена с NSURLRequest mainDocumentURL также. Так что я подкласс NSURL и построен NSURLRequest с этим. Я уже знал, что NSURLRequest который достигает NSURLProtocol startLoading это не тот случай, к которому я скормил UIWebView loadRequest так я реализовал NSURL copyWithZone тоже, наивно ожидая, что система загрузки URL будет использовать его.

Сейчас, NSURLProtocol canInitWithRequest вызывается не один раз, как можно было бы разумно ожидать, но как минимум 4 раза раньше startLoading, Первые 2 раза, входящие NSURLRequest до сих пор содержит мой обычай NSURL реализация. Тогда неудачный внутренний код называется CFURLCopyAbsoluteURL просит absoluteURL из моего обычая NSURL и следующий canInitWithRequest (и последующие startLoading) уже получает совершенно новый NSURLRequest со свежим NSURL в этом. copyWithZone никогда не называется и мой подкласс NSURL потерян.

Прежде чем я откажусь и реализую неполноценное и хрупкое решение с подключением чего-либо непосредственно к строке URL, я хотел бы спросить волшебников более высокого уровня, видят ли они способ, как поймать этот начальный миг на NSURLProtocol радар или как обмануть CFURLCopyAbsoluteURL в переносе моего собственного экземпляра. Я пытался взломать NSURL absoluteURL вернув снова новый экземпляр моего пользовательского класса NSURL, но это не помогло. Я видел какое-то обещание в NSURLProtocol setProperty функциональность, но теперь это выглядит довольно бесполезно. Система загрузки URL создает новые экземпляры всего и счастливо NSURLRequest приехать NSURLProtocol кажется, такой же, как тот, который вошел в UIWebView только случайно.

ОБНОВЛЕНИЕ: хорошо, я хотел, чтобы пост был как можно более коротким, но даже в первом ответе запрашивается техническая справка, поэтому здесь мы идем: у меня есть несколько UIWebView с в приложении. Эти представления могут выполнять запросы одновременно и абсолютно могут выполнять запросы для одного и того же URL. Это как вкладки в настольном браузере. Но мне нужно различать, какие UIWebView было происхождение каждого конкретного NSURLRequest прибывающих в NSURLProtocol, Мне нужно, чтобы контекст передавался с каждым запросом URL. Я не могу просто сопоставить URL-адреса с данными, потому что несколько UIWebViews может загружать тот же URL в любой момент.

ОБНОВЛЕНИЕ 2: Присоединение контекстной информации к NSURL является предпочтительным и, насколько я понимаю, единственным пригодным для использования. Проблема в том, что запросы на ресурсы, на которые ссылаются внутри страницы (изображения и т. Д.), Не проходят через UIWebViewDelegate на всех и в конечном итоге NSURLProtocol непосредственно. У меня нет возможности прикасаться, проверять или изменять такие запросы в любом месте до NSURLProtocol, Единственная контекстная ссылка для таких запросов - их NSURLRequest mainDocumentURL,

4 ответа

Решение

Если есть какой-то способ получить свой оригинал NSURL используется в качестве mainDocumentURL это было бы идеально. Если нет способа предотвратить копирование, я подумаю о следующем взломе в качестве альтернативы:

Перед созданием каждого UIWebView установите для строки агента пользователя уникальное значение. Предположительно, это изменение влияет только UIWebView объекты, которые создаются впоследствии, поэтому каждое представление будет иметь свою собственную отличительную строку агента пользователя.

в NSURLProtocol реализации, вы можете проверить строку агента пользователя, чтобы идентифицировать связанный UIWebView и передать его обработчику реального протокола, используя фактическую строку пользовательского агента (так что сервер не увидит ничего другого).

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

Вы можете передавать параметры через заголовки пользовательских запросов, предполагая, что целевой веб-сайт или поставщик услуг каким-то образом не удаляют их в пути.

Сложной задачей было бы придумать схему кодирования, которая может быть разумно кодирована в строку ASCII для значения поля заголовка, а затем декодирована в фактическое значение, которое вы хотите. Для этого обычай NSValueTransformer казалось бы наиболее подходящим.

Вы говорите, что не можете поставить это на NSURLRequest, но мне не понятно почему из твоего обновленного обсуждения. Это было бы самое естественное место для этого.

  • Воплощать в жизнь webView:shouldLoadWithRequest:navigationType:,
  • Прикрепите дополнительное свойство к предоставленному запросу, используя objc_setAssociatedObject, Затем вернитесь YES, (Было бы неплохо использовать setProperty:forKey:inRequest: здесь, но UIWebView передает нам неизменяемый запрос, поэтому мы можем прикреплять только связанные объекты. Еще один способ, которым UIWebView бледная тень OS X's WebView, который может справиться с этим).
  • в NSProtocol, прочитайте вашу дополнительную собственность, используя objc_getAssociatedObject, Запрос должен быть таким же, как вы были представлены ранее. Вы предполагаете, что это не так. Вы говорите, что запрос на webView:shouldLoadWithRequest:navigationType: отличается от запроса в initWithRequest:cachedResponse:client:?

Я пропускаю другое требование или причуду?

У меня такая же проблема. Наконец, я остановился на решении, предложенном Мэтью (используя строку агента пользователя). Однако, поскольку решение не конкретизировано, я добавляю новый ответ с более подробной информацией. Кроме того, я узнал, что вам не нужно отправлять запрос, чтобы пользовательский агент "прилип". Достаточно пройти через JavaScript, как предлагается здесь.

Следующие шаги работали для меня:

(1) Получить текущий пользовательский агент по умолчанию. Это понадобится вам позже, чтобы вернуть его обратно в запрос в NSURLProtocol. Вам необходимо использовать новый вид веб-просмотра, поскольку получение пользовательского агента сделает его привязанным к веб-представлению, поэтому вы не сможете изменить его позже.

UIWebView* myWebview = [[UIWebView alloc] init];
NSString* defaultUserAgent = [myWebview stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
[myWebview release]; // no needed with ARC, but to emphasize, that the webview instance is not needed anymore

(2) Измените значение в standardUserDefaults (взято отсюда).

NSDictionary* userAgentDict = [NSDictionary dictionaryWithObjectsAndKeys:@"yourUserAgent", @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:userAgentDict];

(3) Сделайте так, чтобы новая строка пользовательского агента прилипала к вашему webView, получая его через javascript, как сделано в (1), но на этот раз с экземпляром webview, с которым вы фактически работаете.

(4) Вернуть пользовательский агент по умолчанию в standardUserDefaults, как здесь.

Другие вопросы по тегам