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'sWebView
, который может справиться с этим). - в
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, как здесь.