swift клиент серверное приложение

Взаимодействие с сервером через API в iOS на Swift 3. Часть 1

Данная статья является обновлением статьи Получение удаленных данных в iOS, написанной в ноябре 2015 с использованием Objective-C и потому морально устарешней. Сейчас же будет приведен код, переписанный на Swift 3 и iOS 10 (последней версией является Swift 4.1 и iOS 11, но мой компьютер их уже не поддерживает).

Краткая теория

Формат url

http заголовок

Браузер преобразует строку url в заголовок и тело запроса. Для http-запроса тело пустое, а заголовок представлен следующим образом

Cхема запроса на сервер

Сначала создается запрос (request), потом устанавливается соединение (connection), посылается запрос и приходит ответ (response).

Делегаты сессии

Все UI операции (связанные с пользовательским интерфейсом) выполняются в главном потоке. Нельзя просто взять и остановить этот поток, пока выполняется какая-то ресурсоемкая операция. Поэтому одним из решений этой проблемы было создание делегатов. Таким образом, операции становятся асинхронными, а главный поток выполняется без остановок. Когда же нужная операция будет выполнена, то будет вызван соответствующий метод делегата. Второе решение проблемы — создание нового потока выполнения.

Как и в оригинальной книге, мы используем делегат, чтобы было операции были разделены между методами более наглядно. Хотя через блоки код получается более компактным.

Описание видов делегатов сессии

Мы используем NSURLSessionDownloadDelegate и реализуем его метод URLSession:downloadTask:didFinishDownloadingToURL:. То есть по сути скачиваем данные с шуткой во временное хранилище, и, когда загрузка завершена, вызываем метод делегата для обработки.

Переход в главный поток

Загрузка данных во временное хранилище осуществляется не в главном потоке, но чтобы использовать эти данные для изменения UI мы перейдем в главный поток.

«Убегающее» замыкание (@escaping)

Так как в силу реализации кода, замыкание которое мы передаем в метод загрузки данных с url, переживет сам метод, то для Swift 3 необходимо явно обозначить его @escaping, а self сделать unowned, чтобы не происходило захвата и удержания ссылки self в этом замыкании. Но это уже нюансы реализации самого языка Swift, а не техонологии получения данных по API.

Переадресация (редиректы)

В некоторых случаях происходят редиректы. Например, если у нас имеется некоторый короткий url, то когда мы вводим его в поисковую строку браузера, браузер сначала идет на сервер, где этот короткий url расшифровывается и отправляется к нам, а затем уже по этому полному url мы переходим на целевой сервер. При необходимости мы можем контролировать эти редиректы с помощью NSURLSessionTaskDelegate, но по умолчанию NSURLSession сама справляется со всеми деталями.

Схема сериализации

Сериализация — это процесс перевода данных из одного вида хранения в другой, без потери содержания. Например, хранятся данные в двоичном виде, чтобы занимать меньше места, а при пересылке по сети их преобразуют в универсальный JSON (JavaScript Object Notation) формат, который уже мы расшифровываем и переводим в объекты нашей среды программирования.

Фигурные скобки обозначают словарь (dictionary), а объекты внутри словаря представлены парами ключ-значение.

API (Application Programming Interface)

В нашем случае API представлен адресом, откуда мы будет получать случайные шутки и форматов JSON ответа, который нам нужно разобрать в удобные для манипулирования структуры

А теперь практика

Весь проект, как и прошлый раз, реализован в коде, без использования storyboard. Весь код написан в 3х файлах: AppDelegate.swift, MainViewController.swift и HTTPCommunication.swift. AppDelegate.swift содержит общую настройку приложения. HTTPCommunication.swift осуществляет настройку соединения (запрос, сессия) и получение данных. В MainViewController.swift эти данные сериализуются для вывода, а также содержится код пользовательского интерфейса.

Создаем пустой проект. Для простоты пишем приложение только для iPhone. Удаляем ViewController.swift, Main.storyboard и в Info.plist также удаляем ссылку на storyboard, а именно строку Main storyboard file base name — String — Main.

По умолчанию App Transport Security в iOS блокирует загрузки из интернета по обычному http (не https), поэтому вносим изменения в Info.plist, как показано ниже. Для этого открываем Info.plist как source code, то и добавляем следующий код:

Мы, как и по умолчанию, запрещает произвольные загрузки: ключ NSAllowsArbitraryLoads в false. Но добавляем в виде исключения наш домен с шутками и все поддомены: значения ключа NSExceptionDomains.

Теперь в AppDelegate.swift переписываем application(_:didFinishLaunchingWithOptions:) следующим образом:

Создаем файл HTTPCommunication.swift. И пишем в нем следующий код.

Теперь распишем код данных функций.

Копируем код retrieveURL(_ url:, completionHandler:)

Копируем код func urlSession(_ session:, downloadTask:, didFinishDownloadingTo:)

Создаем файл MainViewController.swift и копируем следующий код, который создает необходимый интерфейс:

Разобрались с интерфейсом, теперь можно заполнять функционал.

Вот код retrieveRandomJokes()

Теперь запускаем приложение и получаем следующий результат.

Пока мы ждем получения шутки с сайта.

image loader

Наконец, шутка загружена и отображена.

image loader

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

Источник

Архитектура сетевого ядра в iOS-приложении на Swift 3. Часть 1

Для начала немного поясню, о чем пойдет речь в данной статье. Сейчас большинство мобильных приложений, на мой взгляд, являются клиент-серверными. Это означает, что они содержат в составе кода сетевое ядро, отвечающее за пересылку запросов и получение ответов от сервера. Причем речь вовсе не о сетевых библиотеках, которые берут на себя ответственность по «низкоуровневому» управлению запросами вроде пересылки REST-запросов, построения multipart-тела, работы с сокетами, веб-сокетами, и так далее. Речь идет о дополнительной обвязке, которая позволяет управлять запросами, ответами и данными состояния, характерными конкретно для вашего сервера. Именно в вариантах реализации этой обвязки и заключены основные проблемы сетевого уровня во многих мобильных проектах, с которыми мне приходилось работать.

Данная статья ставит целью привести один из вариантов архитектурного решения по построению сетевого ядра приложения, к которому я пришел после долгого времени работы с разными моделями и различными серверными API, и который на данный момент является наиболее оптимальным для задач, встречающихся мне в процессе работы над проектами. Надеюсь, этот вариант поможет вам разработать хорошо структурированное и легко расширяемое сетевое ядро, если вы начинаете новый проект или модифицируете существующий. Также буду рад услышать ваши советы и комментарии по улучшению кода и/или предложенной архитектуры. И да, статья из-за большого объема будет выпущена в двух частях.

Подробности под катом.

Клиент-серверное взаимодействие

В процессе работы приходится разбирать множество самых разных проектов, так или иначе взаимодействующих с серверами, построенными по REST-протоколу. И в большинстве этих проектов наблюдается картина из разряда «кто в лес, кто по дрова», поскольку сетевое ядро везде реализуется по-разному (впрочем, как и другие архитектурные части приложения). Особенно плохо дела обстоят, когда в одном проекте видна рука нескольких разработчиков, сменявших друг друга (да еще и в условиях сжатых сроков, как правило). В таких случаях нередко получается «ядро» довольно жуткого вида, чуть ли не обладающее собственным интеллектом. Попробуем уберечься от таких проблем с помощью предложенного подхода.

С чего начнем?

А в чем тут сложность?

И правда, пока все звучит довольно просто. Однако на деле эти 4 простых пункта содержат в себе дополнительные шаги, требующие дополнительных трудозатрат, и вот как раз тут начинается разнообразие в плане реализации этих самых промежуточных шагов. Немного расширим нашу последовательность, дополнив ее вопросами, возникающими в процессе работы:

Проектирование

Для решения всех приведенных выше вопросов нам понадобятся несколько сущностей:

Для наглядности я отразил весь процесс на схеме (изображение кликабельно):

soa scheme 800image loader

Реализация ядра

Поехали. Создадим Playground и пойдем прямо по шагам из предыдущего раздела. Плюс, допустим, что в большинстве случаев мой сервер возвращает мне JSON. Какой парсер JSON использовать — это на ваше усмотрение, на момент написания статьи, опять-таки, адаптированных под третью версию языка библиотек еще не было, поэтому я использовал самописный парсер GJSON.

Информация об ошибке

Тут все довольно элементарно, класс с парой полей и инициализатором. Финализируем его, в рамках нашей задачи нам ни к чему расширения (конечно, в вашем проекте это может быть по-другому):

Информация об ответе

Чуть более подробный класс, принцип тот же. В данном случае он также соответствует ответу, возвращаемому сервером, это бинарные данные на вход, код ответа, опциональное сообщение + распарсенная ошибка:

Для удобства в вашем проекте, если вы работаете гарантированно с JSON в возвращаемых бинарных данных, то можно расширить этот класс через extension, например, добавив ему поле, возвращающее json-парсер (как пример):

Асинхронная сетевая задача

Вот это уже куда более интересная вещь. Назначение этого класса состоит в хранении всех отдаваемых данных, всех принимаемых данных и своего состояния (завершено/отменено). Также умеет отправлять себя в сеть и вызывать callback после завершения работы. При получении ответа от сервера записывает все полученные данные, в том числе делает попытку трансформации входных данных в JSON-объект, если это возможно, для упрощения последующей обработки. Также, поскольку в дальнейшем набор таких задач мы будем использовать внутри множества Set, нам понадобится реализовать пару системных протоколов.

Пул задач

Как и говорилось ранее, этот класс будет вести учет активных асинхронных задач, что может быть полезно при анализе или отладке. Бонусом он позволяет найти задачу по идентификатору и, например, отменить ее. Через пул задач производится отправка всех тасков в сеть (можно и без него, но тогда таск вам придется хранить где-то самостоятельно, что не очень удобно). Класс делаем синглтоном, разумеется.

Протокол

И завершающая часть, относящаяся непосредственно к сетевому ядру, — обработка ответов. Строгая типизация языка вносит свои коррективы. Мы хотим, чтобы наши классы-парсеры обрабатывали данные и отдавали нам определенный тип данных, а не какой-нибудь Any?. Для этих целей сделаем небольшой генерик-протокол, который в дальнейшем будем реализовывать:

Что дальше?

Собственно, на этом создание сетевого ядра завершено. Его можно спокойно переносить между проектами, только слегка подрихтовывая структуры информации об ответе и ошибке, а также адрес API cервера.

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

Источник

Perfect — REST сервер на Swift

Perfect — как заявляют создатели проекта — Идеальный веб-сервер и инструментарий для разработчиков, использующих Swift язык программирования для создания приложений и других служб REST. Понятно, что «Идеальный» — это не более чем игра слов, но вместе с тем, после знакомства с предлагаемым решеним начинаешь склоняться к тому, что толика правды в этом утверждении есть.

В «прессе» пробегали статьи о том, что на подходе новый язык программирования, который может стать промышленным стандартом с легкой подачи Apple. Язык, который базируется на продвигаемом в массы Swift. Как правило, статьи об этом вызывали больше вопросов, и еще больше раздражения у тех, кому надоело все переучивать (Swift сам по себе довольно быстро меняется). Однако, углубившись в изучение вопроса, становится понятным, что все намного лучше чем, кажется.

Perfect — это не новый язык, серверной разработки. Perfect это серверное окружение, которое позволяет создавать REST API сервисы используя исключительно Swift последней реализации (на момент написания статьи Swift 2.2) Там нет ничего, выходящего за рамки того, что приходится делать ежедневно клиентским разработчикам.

Что будем делать: Создадим страницу-визитку (заглушку), для демонстрации ее при обращении к серверу. Продемонстрируем возможности легкого создания REST API сервисов, которые будут отвечать на GET/POST запросы. Продемонстрируем механизм динамического формирования статических страниц сайта. Причем, делать будем все это на Swift.

Итак, отправным пунктом путешествия станет создание соответствующего окружения. К сожалению, путь который предстоит пройти не столь уж очевиден, и сопряжен с некоторым количеством весьма странных манипуляций — от создания Workspace до изменения схем в Xcode. Пошаговое руководство продемонстрировано в видео подготовленного авторами проекта. Описывать каждый шаг в статье — это скорее тема для хаба «переводы». Мне бы хотелось сосредоточится на практическом применении возможностей Perfect, которые отсутствуют в роликах, или поданы там в чудовищном загадочном виде. К слову сказать, некоторые ролики опубликованные авторами скорее вредны, чем полезны.

Для начала разберемся в понятиях. Perfect состоит из двух частей: Библиотеки сервера (PerfectLib), и запускаемого приложения с минималистическим интерфейсом (Perfect Server). Оба приложения имеют открытый код, и теоретически, Вы сами можете из изменить / допилить под свои нужны. Однако, я строго не рекомендую Вам это делать. Лично у меня постоянно возникают поползновения что-то улучшить. Но следует учитывать, что Perfect не адаптирован для использования совместно с Swift 3. А создатели языка заявляют, что Swift 3 не будет иметь поддержки «сверху в низ», а это значит, что после выхода 3-й версии языка Perfect гарантировано будет обновлен, и Вам придется полностью избавится от уже внесенных изменений, чтоб апнутся на новую версию Swift.

Если Вы еще не дошли до этапа «Hello Perfect!» — самое время это сделать, Скачать необходимое окружение можно здесь. (Часть ссылок на сайте проекта — битые)

Далее, создадим файлы index.html и template.html, и затем добавим их в наш рабочий проект. После добавления зайдем в Build Phases и добавим шаг «New Copy Files Phase»

78e6730afde9411084ea11072a5cb72e

В конечном итоге окно должно будет выглядеть так:

2e4ece5917e04526b47d8749087c6f30

По большей части все эти действия рассмотрены в видеоролике со страницы проекта: www.youtube.com/watch?v=J441eJ40PH4 Однако, рассмотренный случай позволяет либо хостить Web страницы, либо использовать REST API. Мы постараемся объединить обе потребности в одну возможность.

Полностью замените код PerfectServerModuleInit приведенным ниже кодом:

public func PerfectServerModuleInit()
<
Routing.Handler.registerGlobally()

// Root index.html page
Routing.Routes[«*»] =

// Request for static pages
Routing.Routes[«GET», [«/index», «/list»]] =

Теперь необходимо добавить еще несколько классов.

import Foundation
import PerfectLib

class HelloHandler:RequestHandler
<
func handleRequest(request: WebRequest, response: WebResponse)
<
response.appendBodyString(«Hello World!\n»)
response.appendBodyString(«Hello Perfect!\n»)
response.appendBodyString(«Hello Swift Server!\n»)
response.requestCompletedCallback()
>
>

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

import Foundation
import PerfectLib

class StaticPageHandler:RequestHandler
<
var staticPage = «index.html»

internal init(staticPage:String) <
self.staticPage = staticPage
>

func handleRequest(request: WebRequest, response: WebResponse)
<
let file = ContentPage().page(request.documentRoot, pageFile: self.staticPage)

Класс StaticPageHandler позволяет хостить статические страницы с указанными именами. «По-умолчанию» будет использована index.html, но, в принципе, это может быть любая другая страница добавленная в проект.

import Foundation
import PerfectLib

class HelpHandler:RequestHandler
<
func handleRequest(request: WebRequest, response: WebResponse)
<
let list = Routing.Routes.description.stringByReplacingString(«+h», withString: «»)
let html = ContentPage(title:»HELP», body:list).page(request.documentRoot)
response.appendBodyString(«\(html)»)
response.requestCompletedCallback()
>
>

Класс HelpHandler позволяет получить список команд, обрабатываемых сервером. Некоторые другие серверные окружения (к примеру, MS Framework 4.5.1) позволяют получить автодокументируемое REST API сервера. Это очень удобно для разработчиков мобильных приложений — не приходится дергать разработчиков сервера на предмет обслуживания / добавления команд.
Update: В следующей статье мы усовершенствовали механизм документирования API

import Foundation
import PerfectLib

class CarsJson:RequestHandler
<
func handleRequest(request: WebRequest, response: WebResponse)
<
let car1:[JSONKey: AnyObject] = [«Wheel»:4, «Color»:»Black»]
let car2:[JSONKey: AnyObject] = [«Wheel»:3, «Color»:[«mixColor»:0xf2f2f2]]
let cars = [car1, car2]
let restResponse = RESTResponse(data:cars)
response.appendBodyBytes(restResponse.array)
response.requestCompletedCallback()
>
>

Класс CarsJson демонстрирует работу GET/POST запросов со сложной структурой возвращаемых данных. Возвращаемые данные представлены объектом AnyObject.

public class ContentPage:NSObject
<
private var title = «»
private var body = «»
private var footer = «»

public init(title:String=»», body:String=»», footer:String=»Copyright (C) 2016 _MY_COMPANY_NAME_. All rights reserved.»)
<
self.title = title
self.body = body
self.footer = footer
>

var page = template
page = page.stringByReplacingString(«<>», withString: self.title) <br/>page = page.stringByReplacingString(«<<BODY>>», withString: htmlBody) <br/>page = page.stringByReplacingString(«<<FOOTER>>», withString: self.footer)</p> <p>let fileManager = NSFileManager.defaultManager() <br/>let directory = fileManager.currentDirectoryPath <br/>let path = directory + «/\(webRoot)/\(pageFile)» <br/>do <<br/>return try String(contentsOfFile: path) <br/>> catch <<br/>print(«File didn’t create») <br/>></p> <p>Сервисный класс, возвращающий статическую страницу и делающий замену в соответствующих полях. По-умолчанию, используется шаблон template.html, но в принципе, может быть использован любой другой шаблон добавленный в проект.</p> <!-- WP QUADS Content Ad Plugin v. 2.0.84 --> <div class="quads-location quads-ad121126 " id="quads-ad121126" style="float:none;margin:0px 3px 3px 3px;padding:0px 0px 0px 0px;" data-lazydelay="3000"> <!-- WP QUADS - Quick AdSense Reloaded v.2.0.84 Content Yandex async --> <div id="yandex_rtb_R-A-3460488-17" ></div> <script> (function(w, d, n, s, t) { w[n] = w[n] || []; w[n].push(function() { Ya.Context.AdvManager.render({ blockId: "R-A-3460488-17", renderTo: "yandex_rtb_R-A-3460488-17", async: true }); }); t = d.getElementsByTagName("script")[0]; s = d.createElement("script"); s.type = "text/javascript"; s.src = "//an.yandex.ru/system/context.js"; s.async = true; t.parentNode.insertBefore(s, t); })(this, this.document, "yandexContextAsyncCallbacks"); </script> <!-- end WP QUADS --> </div> <p>import Foundation <br/>import PerfectLib</p> <p>public class RESTResponse <br/><</p> <p>public var data:AnyObject = «» // Data from Server to Client <br/>public var status = «» // HTTP status or other code of operation. <br/>public var message = «» // This message Client can show in the Alert View <br/>public var errors = [String]() // All real errors for Console</p> <p>public init(data:AnyObject=»», status:String=»200″, message:String=»», errors:[String]=[]) <br/> <<br/>self.data = data <br/>self.status = status <br/>self.message = message <br/>self.errors = errors <br/>></p> <p>public var array: [UInt8] <</p> <p>let result = [«data»:self.data, «status»:self.status, «message»:self.message, «errors»:self.errors] <br/>return serialization(result) <br/>></p> <p>> catch let error as NSError <<br/>print(error) <br/>></p> <!-- WP QUADS Content Ad Plugin v. 2.0.84 --> <div class="quads-location quads-ad121127 " id="quads-ad121127" style="float:none;margin:0px 3px 3px 3px;padding:0px 0px 0px 0px;" data-lazydelay="3000"> <!-- WP QUADS - Quick AdSense Reloaded v.2.0.84 Content Yandex async --> <div id="yandex_rtb_R-A-3460488-18" ></div> <script> (function(w, d, n, s, t) { w[n] = w[n] || []; w[n].push(function() { Ya.Context.AdvManager.render({ blockId: "R-A-3460488-18", renderTo: "yandex_rtb_R-A-3460488-18", async: true }); }); t = d.getElementsByTagName("script")[0]; s = d.createElement("script"); s.type = "text/javascript"; s.src = "//an.yandex.ru/system/context.js"; s.async = true; t.parentNode.insertBefore(s, t); })(this, this.document, "yandexContextAsyncCallbacks"); </script> <!-- end WP QUADS --> </div> <p>Класс RESTResponse ключевой класс использования REST API и, наиболее спорный. <br/>Возвращаемый объект данных имеет следующую JSON структуру обертки: <br/> <<br/>«data»:<…. >, <br/>«errors»:[… ], <br/>«message»:»», <br/>«status»:200 <br/>> </p> <p>1) Некоторые разработчики категорически неприемлют такой формат. Но каких-либо убедительных аргументов против такого формата возвращаемых данных — я не встречал. Зато, есть определенные преимущества в типизированном формате. Поле «status» по исторической традиции имеет значение «200» в случае успешного выполнения операции. Но, непосредственного отношения к HTTP ответам не имеет. Их всегда можно вычитать из заголовков запроса, и там же они передаются на сторону клиента самим окружением сервера. В поле «status» можно передать значение имеющее смысловую нагрузку в рамках бизнес-логики web-сервиса, а не HTTP слоя. Поле «message» содержит строку, которую нужно продемонстрировать пользователю для уведомления о каких-то действиях на стороне сервера. К примеру, информацию об истечении сессии, особенно тогда, когда с точки зрения клиентского приложения все идет благополучно. Поле «errors»:[] представляет собой массив срок, который уведомляет клиент о каких-либо нештатных ситуациях. Эту информацию полезно сохранять и обрабатывать на стороне клиента или отправлять на специализированный log сервер, для дальнейшей обработки. Ну и наконец поле «data»: <…. >содержит в себе сложную структуру данных — именно то, что ожидается быть полученным на стороне клиента. Основное преимущество такого подхода в том, что если ответ сервера не удовлетворяет заданной схеме — он может быть с чистой совестью отвергнут клиентом. О том как организовать это можно почитать здесь: habrahabr.ru/post/283012 Те из разработчиков, кто является противником описанного подхода могут легко модифицировать свойство «array» так, чтоб возвращались сырые данные, без описанной обертки.</p> <p>2) Я был несколько ошарашен тем подходом, которые предлагали использовать разработчики Perfect для возврата JSON объекта. В предложенном туториале, предполагалось формирование каждого объекта JSON путем последовательного построения дерева через перечисление пар «ключ/значение» вида [JSONKey: JSONValue] (что эквивалентно [String: AnyObject]). При этом предполагается использовать следующий код:</p> <p>public var json: String <br/> <<br/>let result = [«data»:self.data, «status»:self.status, «message»:self.message, «errors»:self.errors]</p> <p>let jsonEncoder = JSONEncoder() <br/>do <<br/>let respString = try jsonEncoder.encode(result) <br/>return respString <br/>> catch let error as NSError <<br/>print(error) <br/>> <br/>return «» <br/>> <br/>….. <br/>response.appendBodyString(self.json) <br/>response.requestCompletedCallback()</p> <p>Однако, этот код абсолютно неработоспособен даже с теми данными, которые сейчас содержаться в классе CarsJson. Кроме того, если следовать логики туториала создателей Perfect, на каждый возвращаемый объект придется писать свой класс для кодирования сложных структур. В предлагаемом мной варианте, вполне очевидно, можно несколькими строками кода сериализировать объект любом степени вложенности. В этом будет не сложно убедится после запуска сервиса.</p> <p style="clear: both">Если все сделано правильно, то мы получим следующую страницу в браузере: <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/2ad/bd2/a20/2adbd2a201f04e428ae5ea44bb5736e6.jpg" alt="2adbd2a201f04e428ae5ea44bb5736e6" title="swift клиент серверное приложение"></p> <!-- WP QUADS Content Ad Plugin v. 2.0.84 --> <div class="quads-location quads-ad121128 " id="quads-ad121128" style="float:none;margin:0px 3px 3px 3px;padding:0px 0px 0px 0px;" data-lazydelay="3000"> <!-- WP QUADS - Quick AdSense Reloaded v.2.0.84 Content Yandex async --> <div id="yandex_rtb_R-A-3460488-19" ></div> <script> (function(w, d, n, s, t) { w[n] = w[n] || []; w[n].push(function() { Ya.Context.AdvManager.render({ blockId: "R-A-3460488-19", renderTo: "yandex_rtb_R-A-3460488-19", async: true }); }); t = d.getElementsByTagName("script")[0]; s = d.createElement("script"); s.type = "text/javascript"; s.src = "//an.yandex.ru/system/context.js"; s.async = true; t.parentNode.insertBefore(s, t); })(this, this.document, "yandexContextAsyncCallbacks"); </script> <!-- end WP QUADS --> </div> <p style="clear: both">Поздороваемся с сервером: введем в строку браузера /hello <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/20c/b49/4c6/20cb494c61a1414686e264c524c2938d.jpg" alt="20cb494c61a1414686e264c524c2938d" title="swift клиент серверное приложение"></p> <p style="clear: both">Запросим сервер информацию о выполняемых командах: <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/ee0/ca3/fc2/ee0ca3fc2e5b48528e1a6a96eba31273.jpg" alt="ee0ca3fc2e5b48528e1a6a96eba31273" title="swift клиент серверное приложение"></p> <p style="clear: both">Обратите внимание, что все команды разбиты на категории — GET, POST. Если Вы будете использовать другие предикаты — то они тоже появятся в этом списке. <br/>Вместе с тем, команда /list одновременно присутствует в обоих списках. Именно она у нас добавлена PerfectServerModuleInit в команду GET и POST, но (!) имеет разные обработчики! <br/>Если обратится к /list из командной строки браузера, будет выведена индексная страница. <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/31b/d7d/904/31bd7d9044d44141843b6407aeac45b4.jpg" alt="31bd7d9044d44141843b6407aeac45b4" title="swift клиент серверное приложение"></p> <p style="clear: both">Но если использовать специальное приложение (например, бесплатный Postman), то в теле Post запроса будем иметь ожидаемый JSON: <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/ef1/2a1/abc/ef12a1abc88f44e38bf029b2a18b8c67.jpg" alt="ef12a1abc88f44e38bf029b2a18b8c67" title="swift клиент серверное приложение"></p> <p>Обратите внимание, что JSON полностью соответствует той структуре, которая была сформирована в классе CarsJson.</p> <p style="clear: both">Но что будет, если из списка GET удалить команду /list <br/>Routing.Routes[«GET», [«/index», «/list»]] = < (_:WebResponse) in return StaticPageHandler(staticPage: "index.html") > </p> <p>Получаем: <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/90f/0f4/ad7/90f0f4ad785a4ae2a90e6b627f217c21.jpg" alt="90f0f4ad785a4ae2a90e6b627f217c21" title="swift клиент серверное приложение"></p> <!-- WP QUADS Content Ad Plugin v. 2.0.84 --> <div class="quads-location quads-ad121129 " id="quads-ad121129" style="float:none;margin:0px 3px 3px 3px;padding:0px 0px 0px 0px;" data-lazydelay="3000"> <!-- WP QUADS - Quick AdSense Reloaded v.2.0.84 Content Yandex async --> <div id="yandex_rtb_R-A-3460488-20" ></div> <script> (function(w, d, n, s, t) { w[n] = w[n] || []; w[n].push(function() { Ya.Context.AdvManager.render({ blockId: "R-A-3460488-20", renderTo: "yandex_rtb_R-A-3460488-20", async: true }); }); t = d.getElementsByTagName("script")[0]; s = d.createElement("script"); s.type = "text/javascript"; s.src = "//an.yandex.ru/system/context.js"; s.async = true; t.parentNode.insertBefore(s, t); })(this, this.document, "yandexContextAsyncCallbacks"); </script> <!-- end WP QUADS --> </div> <p>Таким нехитрым способом, подставляя стартовую страницу, или любую страницу заглушки, можно легко замаскировать использование любой REST команды как правило, если это не сервис с открытым API, в конце разработки команды вида /help удаляют или блокируют. Но можно воспользоваться этой возможностью по-другому: вывести для GET запроса информацию с деталями по команде, заставив работать обработчик статических страниц.</p> <p style="clear: both">Совершенно ожидаемо, что команды /car и /cars вернут такую же структуру но уже в виде веб-страницы. <br/><img decoding="async" onError="javascript: wp_broken_images = window.wp_broken_images || function(){}; wp_broken_images(this);" style="float: left; margin: 0 10px 5px 0;" src="https://habrastorage.org/r/w780q1/files/5e4/e9c/360/5e4e9c3602154a85980dc68b5f356213.jpg" alt="5e4e9c3602154a85980dc68b5f356213" title="swift клиент серверное приложение"></p> <p>При определенной сноровке создание REST API будет не сложнее создания ViewController c необходимой навигацией.</p> <p>Любители экстрима могут попробовать запустить все это на Linux.</p> <p><b>UPDATE:</b> <br/>Появилось пояснение о том, почему в туториале использовалась столь загадочная сериализация JSON ответов — все NS методы (в том числе NSJSONSerialization) перестают работать на Ubuntu.</p> <p><a href="http://habr.com/ru/post/283260/" target="_blank" rel="noopener">Источник</a></p> <!-- WP QUADS Content Ad Plugin v. 2.0.84 --> <div class="quads-location quads-ad121113 " id="quads-ad121113" style="float:none;margin:0px 3px 3px 3px;padding:0px 0px 0px 0px;" data-lazydelay="3000"> <!-- WP QUADS - Quick AdSense Reloaded v.2.0.84 Content Yandex async --> <div id="yandex_rtb_R-A-3460488-4" ></div> <script> (function(w, d, n, s, t) { w[n] = w[n] || []; w[n].push(function() { Ya.Context.AdvManager.render({ blockId: "R-A-3460488-4", renderTo: "yandex_rtb_R-A-3460488-4", async: true }); }); t = d.getElementsByTagName("script")[0]; s = d.createElement("script"); s.type = "text/javascript"; s.src = "//an.yandex.ru/system/context.js"; s.async = true; t.parentNode.insertBefore(s, t); })(this, this.document, "yandexContextAsyncCallbacks"); </script> <!-- end WP QUADS --> </div> </p> </div><!-- .entry-content --> </article><!-- #post-## --> <div class="entry-footer"> <a href="https://businessi.top/tag/blanki/" class="entry-meta__tag">бланки</a> <a href="https://businessi.top/tag/dogovora/" class="entry-meta__tag">договора</a> <a href="https://businessi.top/tag/dokumenty/" class="entry-meta__tag">документы</a> <a href="https://businessi.top/tag/zapolnenie-dokumentov/" class="entry-meta__tag">заполнение документов</a> <a href="https://businessi.top/tag/oformlenie-dokumentov/" class="entry-meta__tag">оформление документов</a> <a href="https://businessi.top/tag/formy/" class="entry-meta__tag">формы</a> </div> <div class="b-share b-share--post"> <div class="b-share__title">Понравилась статья? Поделить с друзьями:</div> <span class="b-share__ico b-share__vk js-share-link" data-uri="http://vk.com/share.php?url=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__fb js-share-link" data-uri="http://www.facebook.com/sharer.php?u=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__tw js-share-link" data-uri="http://twitter.com/share?text=swift+%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82+%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%BD%D0%BE%D0%B5+%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5&url=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__ok js-share-link" data-uri="https://connect.ok.ru/dk?st.cmd=WidgetSharePreview&service=odnoklassniki&st.shareUrl=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__gp js-share-link" data-uri="https://plus.google.com/share?url=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__whatsapp js-share-link js-share-link-no-window" data-uri="whatsapp://send?text=swift+%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82+%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%BD%D0%BE%D0%B5+%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%20https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__viber js-share-link js-share-link-no-window" data-uri="viber://forward?text=swift+%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82+%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%BD%D0%BE%D0%B5+%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%20https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F"></span> <span class="b-share__ico b-share__telegram js-share-link js-share-link-no-window" data-uri="https://telegram.me/share/url?url=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F&text=swift+%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82+%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%BD%D0%BE%D0%B5+%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5"></span> </div> <meta itemscope itemprop="mainEntityOfPage" itemType="https://schema.org/WebPage" itemid="https://businessi.top/swift-klient-servernoe-prilozhenie/"/> <meta itemprop="dateModified" content="2021-12-02"/> <meta itemprop="datePublished" content="2021-11-30T12:14:37+04:00"/> <div id="comments" class="comments-area"> <div id="respond" class="comment-respond"> <div id="reply-title" class="comment-reply-title">Добавить комментарий <small><a rel="nofollow" id="cancel-comment-reply-link" href="/swift-klient-servernoe-prilozhenie/#respond" style="display:none;">Отменить ответ</a></small></div><p class="must-log-in">Для отправки комментария вам необходимо <a href="https://businessi.top/wp-login.php?redirect_to=https%3A%2F%2Fbusinessi.top%2Fswift-klient-servernoe-prilozhenie%2F">авторизоваться</a>.</p> </div><!-- #respond --> </div><!-- #comments --> <li class='li1'><a href='https://businessi.top/kak-sdelat-uspeshnyj-biznes-na-ritualnyx-uslugax/' title='Как сделать успешный бизнес на ритуальных услугах'>Как сделать успешный бизнес на ритуальных услугах</a></li> <li class='li2'><a href='https://businessi.top/vyezdnoj-kejtering-v-rossii/' title='Выездной кейтеринг в России'>Выездной кейтеринг в России</a></li> <li class='li1'><a href='https://businessi.top/riski-biznesa-bez-chego-ne-obojtis-na-puti-k-uspexu/' title='Риски бизнеса: без чего не обойтись на пути к успеху'>Риски бизнеса: без чего не обойтись на пути к успеху</a></li> <li class='li2'><a href='https://businessi.top/swift-dlya-detej-samouchitel-po-sozdaniyu-prilozhenij-dlya-ios-pdf/' title='swift для детей самоучитель по созданию приложений для ios pdf'>swift для детей самоучитель по созданию приложений для ios pdf</a></li> <li class='li1'><a href='https://businessi.top/swf-prilozhenie-chem-otkryt/' title='swf приложение чем открыть'>swf приложение чем открыть</a></li><br> </main><!-- #main --> </div><!-- #primary --> </div><!-- micro --> <aside id="secondary" class="widget-area" itemscope itemtype="http://schema.org/WPSideBar"> <div id="search-2" class="widget widget_search"> <form role="search" method="get" id="searchform" action="https://businessi.top/" class="search-form"> <label class="screen-reader-text" for="s">Поиск: </label> <input type="text" value="" name="s" id="s" class="search-form__text"> <button type="submit" id="searchsubmit" class="search-form__submit"></button> </form></div><div id="custom_html-4" class="widget_text widget widget_custom_html"><div class="textwidget custom-html-widget"><div class="mh-widget widget_text"> <div class="textwidget"> </div> </div> <div class="mh-widget widget_categories sidebar-categories"> <h4 class="mh-widget-title"><span class="mh-widget-title-inner">Сферы бизнеса</span></h4> <ul> <li class="cat-item cat-item-12"><a href="/category/vyrashhivanie/"><i class="fa fa-envira" aria-hidden="true"></i> Выращивание</a></li> <li class="cat-item cat-item-12"><a href="/category/transport/"><i class="fa fa-bus" aria-hidden="true"></i> Транспорт</a></li> <li class="cat-item cat-item-12"><a href="/category/gostinichnyj-biznes/"><i class="fa fa-bed" aria-hidden="true"></i> Гостиничный бизнес</a></li> <li class="cat-item cat-item-13"><a href="/category/dizajn-i-arxitektura/"><i class="fa fa-university" aria-hidden="true"></i> Дизайн и архитектура</a></li> <li class="cat-item cat-item-15"><a href="/category/domashnij-biznes/"><i class="fa fa-home" aria-hidden="true"></i> Домашний бизнес</a></li> <li class="cat-item cat-item-16"><a href="/category/eda-i-napitki/"><i class="fa fa-cutlery" aria-hidden="true"></i> Еда и напитки</a></li> <li class="cat-item cat-item-17"><a href="/category/zhenskij-biznes/"><i class="fa fa-female" aria-hidden="true"></i> Женский бизнес</a></li> <li class="cat-item cat-item-20"><a href="/category/internet-biznes/"><i class="fa fa-at" aria-hidden="true"></i> Интернет-бизнес</a></li> <li class="cat-item cat-item-21"><a href="/category/kafe-i-restorany/"><i class="fa fa-coffee" aria-hidden="true"></i> Кафе и рестораны</a></li> <li class="cat-item cat-item-22"><a href="/category/krasota-i-zdorove/"><i class="fa fa-leaf" aria-hidden="true"></i> Красота и здоровье</a></li> <li class="cat-item cat-item-23"><a href="/category/medicina/"><i class="fa fa-medkit" aria-hidden="true"></i> Медицина</a></li> <li class="cat-item cat-item-26"><a href="/category/obrazovanie-i-nauka/"><i class="fa fa-flask" aria-hidden="true"></i> Образование и наука</a></li> <li class="cat-item cat-item-27"><a href="/category/odezhda-obuv-aksessuary/"><i class="fa fa-shopping-bag" aria-hidden="true"></i> Одежда, обувь, аксессуары</a></li> <li class="cat-item cat-item-28"><a href="/category/otdyx-i-turizm/"><i class="fa fa-tree" aria-hidden="true"></i> Отдых и туризм</a></li> <li class="cat-item cat-item-30"><a href="/category/programmirovanie-i-soft/"><i class="fa fa-code" aria-hidden="true"></i> Программирование и софт</a></li> <li class="cat-item cat-item-29"><a href="/category/proizvodstvo/"><i class="fa fa-cogs" aria-hidden="true"></i> Производство</a></li> <li class="cat-item cat-item-31"><a href="/category/razvedenie/"><i class="fa fa-bug" aria-hidden="true"></i> Разведение</a></li> <li class="cat-item cat-item-32"><a href="/category/razvlecheniya-i-igry/"><i class="fa fa-gamepad" aria-hidden="true"></i> Развлечения и игры</a></li> <li class="cat-item cat-item-33"><a href="/category/reklama/"><i class="fa fa-line-chart" aria-hidden="true"></i> Реклама</a></li> <li class="cat-item cat-item-34"><a href="/category/remont/"><i class="fa fa-wrench" aria-hidden="true"></i> Ремонт</a></li> <li class="cat-item cat-item-35"><a href="/category/selskij-biznes/"><i class="fa fa-paw" aria-hidden="true"></i> Сельский бизнес</a></li> <li class="cat-item cat-item-36"><a href="/category/sredstva-gigieny/"><i class="fa fa-scissors" aria-hidden="true"></i> Средства гигиены</a></li> <li class="cat-item cat-item-38"><a href="/category/stroitelstvo/"><i class="fa fa-th" aria-hidden="true"></i> Строительство</a></li> <li class="cat-item cat-item-39"><a href="/category/sfera-uslug/"><i class="fa fa-handshake-o" aria-hidden="true"></i> Сфера услуг</a></li> <li class="cat-item cat-item-40"><a href="/category/torgovlya/"><i class="fa fa-cart-plus" aria-hidden="true"></i> Торговля</a></li> <li class="cat-item cat-item-42"><a href="/category/finansy/"><i class="fa fa-money" aria-hidden="true"></i> Финансы</a></li> <li class="cat-item cat-item-25"><a href="/category/neobychnye-idei/"><i class="fa fa-superpowers" aria-hidden="true"></i> Необычные идеи</a></li> <li class="cat-item cat-item-46"><a href="/category/kak-nachat-s-nulya/"><i class="fa fa-codepen" aria-hidden="true"></i> Как начать с нуля</a></li> <li class="cat-item cat-item-2"><a href="/category/stati/"><i class="fa fa-file-text-o" aria-hidden="true"></i> Статьи</a></li> </ul> </div></div></div><div id="custom_html-7" class="widget_text widget widget_custom_html"><div class="textwidget custom-html-widget"><!-- Yandex.RTB --> <script>window.yaContextCb=window.yaContextCb||[]</script> <script src="https://yandex.ru/ads/system/context.js" async></script> <!-- Yandex.RTB R-A-3460488-1 --> <div id="yandex_rtb_R-A-3460488-1"></div> <script> window.yaContextCb.push(()=>{ Ya.Context.AdvManager.render({ "blockId": "R-A-3460488-1", "renderTo": "yandex_rtb_R-A-3460488-1" }) }) </script></div></div> </aside><!-- #secondary --> </div><!-- #content --> <footer class="site-footer container" itemscope itemtype="http://schema.org/WPFooter"> <div class="site-footer-inner "> <button type="button" class="scrolltop js-scrolltop"></button> <div class="footer-info"> © Новые Бизнес идеи 2024! <div class="footer-text">Использование материалов с iDeibiznes.ru разрешено только с предварительного согласия с администрацией сайта.<br> <a href="/politika-konfidencialnosti/">Политика конфеденциальности</a> <a href="/pravoobladatelyam/">Правообладателям</a> <a href="/kontakty/">Контакты</a></div> </div><!-- .site-info --> <div class="footer-counters"> </div> </div><!-- .site-footer-inner --> </footer><!-- .site-footer --> </div><!-- #page --> <script>var pseudo_links = document.querySelectorAll(".pseudo-clearfy-link");for (var i=0;i<pseudo_links.length;i++ ) { pseudo_links[i].addEventListener("click", function(e){ window.open( e.target.getAttribute("data-uri") ); }); }</script><script>document.addEventListener("copy", (event) => {var pagelink = "\nИсточник: https://businessi.top/swift-klient-servernoe-prilozhenie";event.clipboardData.setData("text", document.getSelection() + pagelink);event.preventDefault();});</script><script type="text/javascript" src="https://businessi.top/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js" id="wp-polyfill-inert-js"></script> <script type="text/javascript" src="https://businessi.top/wp-includes/js/dist/vendor/regenerator-runtime.min.js" id="regenerator-runtime-js"></script> <script type="text/javascript" src="https://businessi.top/wp-includes/js/dist/vendor/wp-polyfill.min.js" id="wp-polyfill-js"></script> <script type="text/javascript" src="https://businessi.top/wp-includes/js/dist/hooks.min.js" id="wp-hooks-js"></script> <script type="text/javascript" src="https://businessi.top/wp-includes/js/dist/i18n.min.js" id="wp-i18n-js"></script> <script type="text/javascript" id="wp-i18n-js-after"> /* <![CDATA[ */ wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } ); /* ]]> */ </script> <script type="text/javascript" src="https://businessi.top/wp-content/plugins/contact-form-7/includes/swv/js/index.js" id="swv-js"></script> <script type="text/javascript" id="contact-form-7-js-extra"> /* <![CDATA[ */ var wpcf7 = {"api":{"root":"https:\/\/businessi.top\/wp-json\/","namespace":"contact-form-7\/v1"}}; /* ]]> */ </script> <script type="text/javascript" src="https://businessi.top/wp-content/plugins/contact-form-7/includes/js/index.js" id="contact-form-7-js"></script> <script type="text/javascript" src="https://businessi.top/wp-content/themes/root/js/scripts.js" id="root-scripts-js"></script> <script type="text/javascript" src="https://businessi.top/wp-includes/js/comment-reply.min.js" id="comment-reply-js" async="async" data-wp-strategy="async"></script> <script type="text/javascript" id="q2w3_fixed_widget-js-extra"> /* <![CDATA[ */ var q2w3_sidebar_options = [{"sidebar":"sidebar-1","use_sticky_position":false,"margin_top":0,"margin_bottom":0,"stop_elements_selectors":"","screen_max_width":0,"screen_max_height":0,"widgets":["#custom_html-7"]}]; /* ]]> */ </script> <script type="text/javascript" src="https://businessi.top/wp-content/plugins/q2w3-fixed-widget/js/frontend.min.js" id="q2w3_fixed_widget-js"></script> <script type="text/javascript" src="https://businessi.top/wp-content/plugins/quick-adsense-reloaded/assets/js/ads.js" id="quads-ads-js"></script> <script>!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("test",null,e)}catch(t){}return t}()||function(i){var o=!0,s=!1;EventTarget.prototype.addEventListener=function(t,e,n){var r="object"==typeof n,a=r?n.capture:n;n=r?n:{},"touchstart"!=t&&"scroll"!=t&&"wheel"!=t||(n.passive=void 0!==n.passive?n.passive:o),n.capture=void 0!==a?a:s,i.call(this,t,e,n)}}(EventTarget.prototype.addEventListener);</script> <style> @media only screen and (min-device-width : 320px) and (max-device-width : 1199px){ .flat_ads_block { display: none; }} </style> <!--<div class="flat_ads_block" style="width: 300px; height: 250px;" id="flat_ads_block_id_1"> <div class="flat_slide_cross"></div> <div class="wrap_flat_ads_block"> <div class="dvxtm5d4ae5adba5b5" > <!-- Y2202221-1 --> <!--</div><style type="text/css"> @media screen and (min-width: 1201px) { .dvxtm5d4ae5adba5b5 { display: block; } } @media screen and (min-width: 993px) and (max-width: 1200px) { .dvxtm5d4ae5adba5b5 { display: block; } } @media screen and (min-width: 769px) and (max-width: 992px) { .dvxtm5d4ae5adba5b5 { display: block; } } @media screen and (min-width: 768px) and (max-width: 768px) { .dvxtm5d4ae5adba5b5 { display: block; } } @media screen and (max-width: 767px) { .dvxtm5d4ae5adba5b5 { display: block; } } </style> </div> </div> <!-- было скрыто script type="text/javascript"> $(document).ready(function() { console.log('ready'); $(window).on('scroll', function(){ if($(this).scrollTop() > 1000){ $('#flat_ads_block_id_1').addClass('left_slide_flat_panel'); } else { $('#flat_ads_block_id_1').removeClass('left_slide_flat_panel'); } }); }); </script было скрыто --> <!-- <style> #flat_ads_block_id_1 { position: fixed; right: calc(100% + 22px); bottom: 0px; transform-origin: center; transform: translateX(0); transition: all 0.3s ease; z-index: 998; } #flat_ads_block_id_1.left_slide_flat_panel { transform: translateX(calc(100% + 22px)); } #flat_ads_block_id_1 .flat_slide_cross { position: absolute; width: 22px; height: 22px; top: -22px; right: -10px; z-index: 1; background: #000 url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy1TVkctMjAwMTA5MDQvRFREL3N2ZzEwLmR0ZCI+CjxzdmcgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiB2aWV3Qm94PSIwIDAgMTc1LjAwMDAwMCAxNzYuMDAwMDAwIgogcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCI+CjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDAwMDAwLDE3Ni4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiCmZpbGw9IiNmZmYiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik02MzIgMTEyOCBsLTYyIC02MyA5MSAtOTEgOTEgLTkxIC05MSAtOTUgLTkwIC05NSA2NSAtNjQgNjQgLTY0IDkyCjkzIDkzIDkyIDkyIC05MiA5MyAtOTIgNjAgNTkgYzMzIDMzIDYwIDY0IDYwIDY5IDAgNiAtMzkgNTAgLTg3IDk4IGwtODggODgKODggODggYzQ4IDQ4IDg3IDkyIDg3IDk4IDAgMTIgLTExMiAxMjQgLTEyNSAxMjQgLTUgMCAtNDkgLTM5IC05NyAtODcgbC04OAotODggLTg4IDg4IGMtNDggNDggLTkwIDg3IC05MiA4NyAtMyAwIC0zMyAtMjggLTY4IC02MnoiLz4KPC9nPgo8L3N2Zz4K) no-repeat center / cover; cursor: pointer; border-radius: 100%; } #flat_ads_block_id_1 .flat_slide_cross:hover { background: #444 url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy1TVkctMjAwMTA5MDQvRFREL3N2ZzEwLmR0ZCI+CjxzdmcgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiB2aWV3Qm94PSIwIDAgMTc1LjAwMDAwMCAxNzYuMDAwMDAwIgogcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCI+CjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDAwMDAwLDE3Ni4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiCmZpbGw9IiNmZmYiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik02MzIgMTEyOCBsLTYyIC02MyA5MSAtOTEgOTEgLTkxIC05MSAtOTUgLTkwIC05NSA2NSAtNjQgNjQgLTY0IDkyCjkzIDkzIDkyIDkyIC05MiA5MyAtOTIgNjAgNTkgYzMzIDMzIDYwIDY0IDYwIDY5IDAgNiAtMzkgNTAgLTg3IDk4IGwtODggODgKODggODggYzQ4IDQ4IDg3IDkyIDg3IDk4IDAgMTIgLTExMiAxMjQgLTEyNSAxMjQgLTUgMCAtNDkgLTM5IC05NyAtODcgbC04OAotODggLTg4IDg4IGMtNDggNDggLTkwIDg3IC05MiA4NyAtMyAwIC0zMyAtMjggLTY4IC02MnoiLz4KPC9nPgo8L3N2Zz4K) no-repeat center / cover } </style> --> </body> </html>