Отличия в адаптации сайта и AJAX веб-приложения для iOS

в 0:20, , рубрики: cookies, iOS, ipad, iphone, ipod, javascript, session, веб-приложение, Веб-разработка, разработка под iOS, метки: , , , , , ,

Отличия в адаптации сайта и AJAX веб приложения для iOSЕсть сейчас такая тенденция — делать в сайтах поддержку планшетов iPad и других устройств на iOS: iPhone, iPod. Но если для сайтов это достаточно просто, при хорошей верстке, можно добавить пару тегов в head и готово, то для веб-приложений, где есть сессии с использованием Cookies, все обстоит сложнее и есть подводные камни. Итак, возможно, еще не все знают, что в мобильном Safari можно нажать кнопку меню (со стрелкой, как на рисунке) и выбрать там «Добавить в Домой» / «Add to Home Screen», тогда для сайта появится иконка на рабочем столе. Но иконка будет просто запускать Safari с этим сайтом, а вот если добавить пару известных тегов (см. ниже), то все элементы управления Safar будут скрыты и приложение будет работать на полный экран, как обычные нативные приложения iOS. Так вот основная выявленная проблема в том, что в этом режиме сессия все время сбрасывается. Стоит переключится на другое приложение или рабочий стол, даже просто перейти по ссылке, и опять вернуться в веб-приложение, как страница перегрузится и сессионной Cookie уже не будет, нужно логиниться заново. Эту проблему то мы и решим.

По хорошему, полноэкранный режим доступен только для полностью динамических AJAX веб-приложений, которые не перегружают экран целиком и не переходят по ссылкам даже в рамках своего домена. Если же адаптируется обычный сайт, где навигация происходит при смене URL и перегрузке страниц, то и fullscreen не доступен и куки не исчезают.

Рецепт для простого сайта

Решение это известно, я приведу его только потому, что ниже дополню его отличиями для веб-приложения.
Тут документация по дополнительным мета-тегам Safari, а вот нужные нам мета-теги для вставки в head:

<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<link rel="apple-touch-icon-precomposed" href="/favicon.png">

Картинка /favicon.png станет иконкой на рабочем столе.

Решение для WebApplication

Как я вскользь упоминал, для веб-приложений нужно выдержать дополнительные условия: приложение не должно переходить по ссылкам a href, это приводит к открытию ссылок в браузере и выходу из фулскрин режима. Но вот делать window.location.reload(true); можно и даже window.location = "/demo/path"; вполне разрешен из JavaScript. При этих переходах кукизы не теряются и все хорошо.

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

function PersistCookie(SessionCookeiName) {
	if (localStorage && (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/iPad/i))) {
		var CookieSession = document.cookie.match(new RegExp(SessionCookeiName+"=[^;]+")),
			LocalSession = localStorage.getItem(SessionCookeiName);
		if (CookieSession) {
			CookieSession = CookieSession[0].replace(SessionCookeiName+"=","");
			if (LocalSession!=CookieSession) localStorage.setItem(SessionCookeiName,CookieSession);
		} else if (LocalSession && LocalSession!=CookieSession) {
			document.cookie = SessionCookeiName+"="+LocalSession+"; path=/";
			window.location.reload(true);
		}
	}
}

Как видно из кода, у нас есть два места хранения сессионной переменной: document.cookie и localStorage, мы читаем из обоих, а пишем туда, где кода небыло. В случае, если код есть в обоих местах, то предпочтение отдается document.cookie, т.к. может так случиться, что сервер заменит сессионную переменную и нам ее нужно записать поверх той, что уже есть в localStorage. Пример вызова: PersistCookie(«SID»); В параметрах передается имя сессионной куки. Вызов нужно делать при загрузке страницы, но оборачивать в событие «onload» или в jQuery.ready() не обязательно. Для PHP имя сессионной куки «PHPSESSID», для ASP.NET «ASP.NET_SessionId» и т.д. но может меняться в настройках сервера или программно. При отлогинивании пользователя нужно не забыть сделать if (localStorage) localStorage.clear(); чтобы кукиз не вернулся. Еще можно отключить проверку navigator.userAgent, чтобы код работал не только в iOS, но я не исследовал, будет ли это полезно или вредно.

P.S. Вообще, я нашел описание проблемы с кукизами в англоязычных форумах, и один робкий совет: устанавливать время жизни сессионного куки со стороны сервера. Не знаю, почему такое советовали, я пробовал это делать, совет не работает, возможно, он работал на каких-то старых версиях iOS или человеку только показалось, что этот метод сработал. Вообще, сессионные куки не должны иметь время жизни, т.е. поля Expires по спецификации у них нет, иначе они перестают быть сессионными.

Автор: MarcusAurelius

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