Разбираемся с NSURL / NSURLComponents

в 9:15, , рубрики: nsurl, nsurlcomponents, разработка под iOS

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


Существуют, давайте их назовем «одномерными», типы данных: числа, строки, содержащие в себе по сути несколько значений, которые могут быть извлечены при помощи математических действий или простым парсингом. В качестве примера можно привести шестнадцатеричную запись цвета #EE8262, которая содержит компоненты для красного, синего и зеленого цвета, или, например, регулярное выражение, которое при помощи пары символов повзоляет искать сложные подстроки.

Среди всех «одномерных» типов данных безусловным победителем является URI. Посудите сами, здесь в одной человекочитаемой строчке находится достаточно данных, чтобы понять где находится та или иная информация, которая была, есть или когда-либо будет на компьютере.

Обычно URI включает в себя следующие компоненты: схему, некоторую иерархическую часть, запрос и фрагмент:
<scheme name> : <hierarchical part> [ ? <query> ] [ # <fragment> ]

Многие протоколы, как например HTTP, описывают некоторую заданную структуру такую как имя пользователя, пароль, порт и путь в иерархической части:

foo:// username: password @ example.com :8042 /over/there/index .dtb ?type=animal&name=narwhal #nose
scheme username password hostname port path extension query fragment

Для уверенной работы с сетевым взаимодействием необходимо хорошее знание URL компонентов. Для программиста это означает свободное использование URI компонентов стандартной библиотеки.

В Foundation URL'ы представлены классом NSURL.

NSURL обычно инстанцируется при помощи классового метода URLWithString:

NSURL *url = [NSURL URLWithString:@"http://example.com"];

Если строка, которую отдаем, является не валидным URL, то получим nil.

NSString имеет некий рудиментарный функционал для манипуляций с путями, об этом можно почитать здесь (там в том числе рассказывается про то, что Apple советует переходить от NSString к NSURL-like API для таких классов как NSFileManager). К сожалению, миграция от NSString к NSURL не идет так гладко, как хотелось бы. Hecмотря на то, что преобразование из NSString в NSURL не самый удобный шаг, это — правильно. Если значение — URL, оно должно храниться и передаваться как NSURL; использование класса не по назначению — признак лени и пример плохого дизайна API.

Кстати, что думаете насчет использования @@ в качестве литерала для NSURL (т.е. @@«example.com»)?

Также у NSURL есть классовый метод +URLWithString:relativeToURL:, который используется для создания URL из строки относительного некоторого базового URL. Поведение этого метода может быть не всегда очевидным в случае, когда относительный путь кончается на `/`

Вот несколько примеров, которые покажут как этот метод работает:

NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];

[NSURL URLWithString:@"foo" relativeToURL:baseURL];
// http://example.com/v1/foo

[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL];
// http://example.com/v1/foo?bar=baz

[NSURL URLWithString:@"/foo" relativeToURL:baseURL];
// http://example.com/foo

[NSURL URLWithString:@"foo/" relativeToURL:baseURL];
// http://example.com/v1/foo

[NSURL URLWithString:@"/foo/" relativeToURL:baseURL];
// http://example.com/foo/

[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL];
// http://example2.com/

URL Components

NSURL предоставляет методы доступа ко всем URL компонентам, описанным в RFC 2396:

  • absoluteString
  • absoluteURL
  • baseURL
  • fileSystemRepresentation
  • fragment
  • host
  • lastPathComponent
  • parameterString
  • password
  • path
  • pathComponents
  • pathExtension
  • port
  • query
  • relativePath
  • relativeString
  • resourceSpecifier
  • scheme
  • standardizedURL
  • user

Документацию и примеры можно посмотреть в докуменентации — это неплохой способ познакомиться со всеми компонентами.

Отдельно надо сказать, что хоть имя пользователя и пароль можно хранить в URL, лучше использовать NSURLCreditnal или хранить их в связке ключей.

NSURLComponents

NSURLComponents появились в iOS 7 и Mac OS 10.9, честно говоря, лучшим названием для этого класса было бы NSMutableURL. Из-за отсутствия документации, этот класс стал одним из самых «секретных» дополнений в Foundation.

Инстансы NSURLComponents создаются при помощи:(+componentsWithString: и +componentsWithURL:resolvingAgainstBaseURL:). Само собой можно просто послать alloc и init, без каких либо аргументов, чтобы создать пустой контейнер.

Различие между NSURL и NSURLComponents в том, что у последнего свойства изменяемые. Это предоставляет безопасный и простой способ изменить отдельные компоненты в URL:

  • scheme
  • user
  • password
  • host
  • port
  • path
  • query
  • fragment

Попытка установить неправильную схему или выбрать отрицательное число для порта закончится исключением.

Также у NSURLComponents есть изменяемые percent-encoded варианты каждого компонента.

  • percentEncodedUser
  • percentEncodedPassword
  • percentEncodedHost
  • percentEncodedPath
  • percentEncodedQuery
  • percentEncodedFragment

При получении этих свойств мы получим их в percent-encoded виде. Изменяя эти свойства нужно отдавать уже заэнкоженную строчку, иначе — исключение. `;` — является корректным символом, но рекомендуется его также энкодить для лучшей своместимости с NSURL. (-stringByAddingPercentEncodingWithAllowedCharacters: заэнкодит все `;` если передать URLPathAllowedCharacterSet)

Percent-Encoding

NSURLtoll-free bridged для CFURLRef. Последний является низкоуровневым API на С, который эффективно повторяет функциональность NSURL, но за исключением CFURLCreateStringByAddingPercentEscapes и CFURLCreateStringByReplacingPercentEscapesUsingEncoding:

CFURLCreateStringByAddingPercentEscapes: создает копию строчки, заменяя конкретные символы на соответствующие им заэскейпенные последовательности в соответствующей кодировке.

CFStringRef CFURLCreateStringByAddingPercentEscapes (
   CFAllocatorRef   allocator,
   CFStringRef      originalString,
   CFStringRef      charactersToLeaveUnescaped,
   CFStringRef      legalURLCharactersToBeEscaped,
   CFStringEncoding encoding
);

CFURLCreateStringByReplacingPercentEscapesUsingEncoding: делает все наоборот заменяет заэскейпенные последовательности на соответствующие символы.

CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding (
   CFAllocatorRef   allocator,
   CFStringRef      origString,
   CFStringRef      charsToLeaveEscaped,
   CFStringEncoding encoding
);

URL для закладок

Последний вопрос, который хотелось бы обсудить — URL'ы для закладок, это те пути, которые можно использовать, чтобы безопасно ссылаться на файлы между запусками приложения. Можно думать о них как о файловых дескрипторах.

Закладка — закрытая файловая структура, которая заключена в объект NSData, она описывает местоположене файла. В то время как путь и ссылочный URL потенциально ненадежны, закладка может быть использована чтобы воссоздать URL, если даже файл был перемещен или переименован.

Больше про ссылки можно прочитать в «Locating Files Using Bookmarks» из яблочной документации.

Автор:

Источник

Поделиться

* - обязательные к заполнению поля