Category: iOS

11
Май
2022

Как повысить качество скриншота через appium webdriver на iOS?

использую element.screenshot_as_png(), где элемент взят по селектору
На iOS отрабатывает крайне плохо, много блюра, левых пикселей, цвет плывет, сглаживание какое-то присутствует

Если вручную заскринить, качество в разы лучше:
(Не плывет …

15
Апр
2022

Не работает API в React приложении на github pages в ios?

Залил я значит недавно react приложение на gh-pages. Все работало отлично до того момента, когда я открыл его на своём ios девайсе, не работают почти все API запросы на сервер. Пробовал открывать не в сафари, а в хроме, но все равно не раб…

14
Апр
2022

Баг видео и текста в safari ( swiper )

Суть бага в том, что когда мы свайпаем или переключаемся между слайдами в swiper slider, на 1 сек пропадает текст и потом появляется. Данный баг есть только на ios устройствах. Заметил один момент, все сайдеры разные и только 2, у которых …

12
Янв
2022

Перестали приходить push-уведомления на IPhone

Неделю назад пуши шли стабильно. На днях перестали
Вот простой код для отправки тестового пуш-уведомления (раньше работало)
На андроид идет нормально. При это на обоих платформах пишет success
{"multicast_id":********,"succe…

12
Янв
2022

Перестали приходить push-уведомления на IPhone

Неделю назад пуши шли стабильно. На днях перестали
Вот простой код для отправки тестового пуш-уведомления (раньше работало)
На андроид идет нормально. При это на обоих платформах пишет success
{"multicast_id":********,"succe…

09
Янв
2022

Возможно ли добавить базу данных Википедии в мобильное приложение?

хочу разработать мобильное приложение, которое будет брать информацию с Википедии, возможно ли это? Пишу на Swift. Если возможно, то напишите поподробнее как добавить данные и тд.

04
Дек
2021

🍏 Загрузите приложение в App Store за 6 шагов: практическое руководство для начинающих

Начинающие iOS-разработчики часто испытывают проблемы с размещением приложений в App Store. Рассказываем, как можно это исправить за 6 простых шагов.

App Store – одна из крупнейших платформ для распространения, продвижения и продажи приложений. Ее пользовательская база – около миллиарда человек по всему миру. Разместив программу в этом магазине, вы получите отличный шанс привлечь миллионы потенциальных клиентов.

Корпорация Apple довольно разборчива и неподготовленный разработчик может столкнуться с рядом сложностей при попытке это сделать. В небольшой пошаговой инструкции мы попытаемся обойти все подводные камни и рассмотреть наиболее важные при работе с платформой моменты. Поехали!

1. Прочтите рекомендации Apple

Разберем ситуацию, когда созданное вами приложение готово к запуску. Период разработки остался позади вместе с бессонными ночами и литрами выпитого кофе. Программа отлично работает, хорошо продумана и вы готовы представить ее человечеству, официально опубликовав на App Store. Сделать это с первого раза непросто, но, следуя нашим советам, вы сможете грамотно подготовиться к загрузке.

Перед началом процедуры заливки файлов на сервер тщательно изучите App Store Review Guidelines. Это официальные рекомендации по загрузке приложений от компании-разработчика: там вы узнаете основные причины отклонения подаваемых заявок модераторами.

Процесс размещения приложения в App Store выглядит так:

  • Получение лицензии разработчика.
  • Выполнение требований магазина и финальная доработка приложения.
  • Получение (создание) идентификатора приложения, сертификата распространения и профиля обеспечения.
  • Настройка приложения с загрузкой двоичного файла.
  • Ожидание.

Далее разберем каждый пункт списка более детально.

Больше полезной информации вы найдете на нашем телеграм-канале «Библиотека мобильного разработчика».

2. Создайте аккаунт разработчика

Для получения возможности размещения программы в магазине вам необходимо создать аккаунт iOS-разработчика.

Учетная запись Apple-developer обязательна и стоит $99 в год. После ее оформления вы сможете использовать App Store Connect для управления своими приложениями.

Вам понадобится устройство Mac или виртуальная машина, чтобы загрузить приложение в App Store. Единственный способ это сделать – через XCode или Transporter, которые могут быть установлены только на MacOS.

Войдите в систему под своим Apple ID, если у вас его нет, создайте.


Заполните регистрационную форму.



В разделе Romanized Contact Information вас попросят продублировать уже введенную информацию.


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

Выберите тип лицензии (для индивидуального использования или корпоративный вариант).


Оплатите регистрационный взнос.


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

3. Проведите финальную доработку приложения

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

Apple в помощь разработчикам выпустила несколько обязательных к прочтению гайдов. Из наиболее важных можно отметить уже знакомый нам App Store Review Guidelines, а также Human Interface Guidelines (совету по грамотному дизайну программы).

Вот некоторые из обязательных к исполнению правил:

  • Приложение не должно использовать частные API.
  • Не должно сбоить.
  • Не должно копировать функциональность нативных приложений.
  • Не должно использовать микрофон или камеру без разрешения пользователя.
  • Не должно использовать картинки и музыку, защищенные авторскими правами.

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

4. Выполните предварительные требования

Прежде чем отправлять программу в App Store, необходимо сделать следующее:

  • Получить сертификат распространения (Distribution Certificate), позволяющий идентифицировать вас или вашу команду. Это профиль iOS, используемый для распространения продукции.

  • Создать профиль обеспечения (review provision profile). Чтобы приложение начало работать в экосистеме iOS, оно должно быть проверено и одобрено Apple. Как протестировать его на реальном устройстве перед отправкой на проверку? Профиль обеспечения позволяет загрузить приложение на физическое железо до одобрения модераторами.
  • Получить идентификатор приложения (App ID). После его создания в вашем арсенале появятся дополнительные возможности улучшения функциональности программы (например, push-уведомления и вход через Apple ID). Вам не обязательно настраивать их сразу, это можно сделать позже.


5. Настройте приложение

Когда вы разберетесь с описанными выше идентификаторами и сертификатами на портале разработчиков Apple, можно будет перейти к App Store Connect (бывший iTunes Connect) – платформе, на которую фактически загружаются бинарные пакеты приложений.


Нажмите на вкладку MyApps и оказавшись на новой странице создайте новое приложение, нажав кнопку +.


После создания приложение необходимо настроить, заполнив соответствующие поля.

Перечислим наиболее важные из них:

  • Иконки. Очевидно, что значок вашего приложения – это его лицо. Убедитесь, что вы легально используете изображение подходящего размера и качества.
  • Скриншоты. Улучшите свое приложение с помощью снимков экрана и превью. Вам необходимо будет отправить снимки экрана для устройств разного размера: iPhone 6,5 дюймов (1242 x 2688 пикселей или 1284 x 2778 пикселей), iPhone 5,5 дюймов (1242 x 2208 пикселей) и iPad (2048 x 2732 пикселей). Будьте внимательны при выборе скриншотов, поскольку иногда это единственный критерий, по которому потребитель решает – загружать приложение или нет.
  • Название приложения, категория, подробное описание и дополнительные ключевые слова. Не стоит недооценивать важность описания: первые 3 строки должны заинтересовать пользователя, ведь это та часть, которую он увидит не нажимая на вкладку прочитать больше. Будьте точны, ясны и кратки, дайте пользователям понять основную ценность приложения.
  • Возрастной рейтинг. Определите порог по возрасту, исходя из типа контента приложения.
  • Цена и доступность: Apple позволяет выставить цену приложения, выбрав один из уровней (от бесплатного до 1 000 $+). Вы также можете выбрать страны, в которых приложение будет доступно (по умолчанию оно доступно во всех странах мира).
  • Права на контент: здесь необходимо подтвердить, что вы не используете нелегальный контент.
  • Отзывы. Здесь добавляется возможность работать с отзывами от имени администратора приложения.
  • Контактная информация. Ее надо заполнить, чтобы модераторы App Store могли с вами связаться при необходимости.
  • Активация платных функций (покупки и подписки). Сюда нужно следующие сведения: информацию о физическом или юридическом лице, необходимые соглашения, банковские реквизиты, а также информацию о налоговой отчетности или налоговую декларацию.
Примечание
Этот раздел необходимо заполнить на английском языке максимально подробно. Здесь описывается назначение и функциональные возможности приложения, а также индивидуальные особенности проекта (разрешения, сертификаты и другие необходимые документы). Чем больше детальной информации вы добавите, тем меньше вероятность неудачной проверки.

  • Правила конфиденциальности. Если ваше приложение требует создания учетных записей пользователей, вы должны гарантировать их конфиденциальность.
В этом пункте отметим некоторые нововведения:
Вместе с выходом iOS 14 Apple объявила об изменении правил конфиденциальности приложений. Основным нововведением стало принятие принципа прямого согласия пользователя на доступ к уникальному идентификатору его устройства (IDFA), на отслеживание запросов и передачу данных третьим лицам. Теперь разработчик обязан предоставить в App Store Connect исчерпывающую информацию о процессе сбора данных в программе, а также добавить ссылку на политику конфиденциальности. Все эти сведения необходимо отразить на стартовой странице приложения, чтобы пользователь еще до скачивания мог с ними ознакомиться.

  • Дизайн. У вашей программы должен быть отличный внешний вид. Пользовательский интерфейс должен быть четким и легким, а структура приложения должна обеспечивать интуитивно понятную и удобную навигацию.

После того, как все флажки проставлены и внесены необходимые правки, ваше приложение можно отправлять на модерацию.

6. Ожидайте

Команде Apple Review Team потребуется некоторое время, чтобы рассмотреть приложение, а пока оно будет находится в статусе awaiting verification. Сам процесс занимает несколько дней, после чего вы получите сообщение с результатами проверки.

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

***

Тим Кук как-то сказал: «Жить на земле стало лучше, потому что для решения любой проблемы уже создано свое приложение». Это действительно так. Используйте наше небольшое руководство для размещения программы в экосистеме Apple, а если вам нужна более подробная информация, ее можно найти на официальном сайте компании. Удачи!

15
Ноя
2021

После получения доступов открывается стандартный вид камера по вверх wkwebview swift

У меня есть сайт на js, который использует камеру для сканирования QR-кода. И веб-приложение на swift которое тянет мой сайт через wkwebview.
x-код http://joxi.ru/5md9aqpCadREo2
Проблема в том, что когда запрашиваю использование камеры из …

05
Ноя
2021

Как декодировать сжатую строку из PHP в Swift?

У меня есть текстовый файл сжатый с помощью функции zlib в php:
base64_encode(gzcompress(‘привет’)) // "eJy7sP9iw4UdFzZd2HqxCQA6EAjD"

Теперь мне нужно прочитать этот файл в iOS приложении.
Для этого я попытался использовать библ…

05
Ноя
2021

Как декодировать сжатую строку из PHP в Swift?

У меня есть текстовый файл сжатый с помощью функции zlib в php:
base64_encode(gzcompress(‘привет’)) // "eJy7sP9iw4UdFzZd2HqxCQA6EAjD"

Теперь мне нужно прочитать этот файл в iOS приложении.
Для этого я попытался использовать библ…

04
Ноя
2021

🍏 Самоучитель для начинающих: освойте основы Swift за 30 минут

Думаете, чтобы освоить Swift нужно много времени? И да, и нет. В изучении языков программирования важна практика, но что может сделать начинающий за 30 минут? Только познакомиться с базовым синтаксисом с помощью нашего самоучителя.

Создать Playground

Запустим Xcode и создадим Playground. Назовем его “BasicSwift”:

File → New → Playground…

⌥⇧⌘N

Swift Playground – это такой тип файлов, который представляет из себя игровую площадку для ваших экспериментов с кодом Swift. Результат исполнения каждой строчки кода вы можете наблюдать в боковой панели (Sidebar).

Переменные, константы, типы

Переменные обозначаются с помощью ключевого слова var

        var hello = "Hello Proglib"
    

Чтобы объявить константу используется ключевое слово let

        let brandName = "Proglib"
    
Swift – это статистический типизированный язык, а это значит, что мы должно явно задать тип каждой переменной во время компиляции.

Указывать тип переменной необязательно. Компилятор Swift автоматически его определит. Эта особенность называется интерференцией типов(type inference).

Swift предлагает следующие базовые типы: Int или UInt, Double, Float, String, Character, Bool, Tuples, Optional.

Множество типов фундаментально и чаще всего используется для объявления переменных. Забегая вперед скажу, что вы также можете создавать и свои собственные типы.

Мы не будет детально разбирать их все! Посвятим этой теме следующую 30-ти минутку.

Укажем тип нашей константы.

        let brandName: String = "Proglib"
    
Больше полезной информации вы можете найти на нашем телеграм-канале «Библиотека мобильного разработчика».

Интерполяция строк

Давайте теперь выведем имя бренда в консоль при помощи функции print().

        print(brandName)
    

Интерполяция – это способ объединить переменные и константы внутри строки.

Теперь присвоим переменной hello новое значение, и выведем в консоль.

        hello = "Hello \(brandName)"
print(hello)
    

Вот еще пример:

        let name = "Ivan" 
var age: UInt8 = 30
print("\(hello). My name is \(name). I am \(age)")

    

Базовые операторы

Здесь все интуитивно. Предлагаю вам поэкспериментировать с ними самостоятельно и посмотреть на результаты.

Арифметические операторы

+ сложение
вычитание
/ деление
% деление по модулю
        var a = 1, b = 2
var result = a + b
// etc

    

Операторы присвоения

= , +=, -=, /=, %=

        var h = 10
h += 1
h /= 2
h %= 2
// etc

    

Операторы сравнения

Операторы сравнения в качестве результата возвращают значения типа Bool

==, ≠, >, <, ≥, ≤

        let condition = 2 > 3
let condition2 = 1 != 0
let resultCondition = condition == condition2
// etc

    

Логические операторы

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

Рассмотрим на примере:

        // логическое И
print(true && true)      // true
print(true && false)     // false

// логическое ИЛИ
print(true || false)      // true

// логическое НЕ – инвертирует булево значение
print(!true)              // false

    

Это еще не все операторы, которые существуют в Swift. Остальные мы рассмотрим в контексте целей их предназначения.

Коллекции

Swift предлагает нам три основных типа коллекций: Array(Массивы), Dictionary(Словари) и Set(Множества). Сейчас мы рассмотрим первые два.

Array

Используйте квадратные скобки [], чтобы создавать массивы(Array) и словари(Dictionary).

Получить доступ к элементам массива можно написав индекс внутри скобок.

        let digits: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9]
 print(digits[3])

    

Попробуйте изменить значение в массиве digits. Что произойдет?

        digits[0] = 1
    
🪲
Cannot assign through subscript: ‘digits’ is a ‘let’ constant

Поскольку мы объявили массив как константу, то и все его значения становится не изменчивыми (immutable), как и его размер. А если мы объявим массив как переменную, то он будет изменчивым (mutable). Такое свойство называют изменчивостью коллекций (mutability of collection).

Это утверждение справедливо и для Словарей.

        // mutable
var numbers = [50, 10, 20, 34, 45]
print(numbers[0])
numbers[0] = numbers[0] + 1

    

Dictionary<Key, Value>

Создадим словарь sunSystemData, в который добавим данные о солнечной системе.

Пусть ключами (key) данного словаря будут номера объектов солнечной системы, а значениями (value) названия объектов. Поскольку звезда рождается первой, то будем считать, что ключ со значением 0 всегда указывает на звезду. Остальные объекты следуют в порядке по отношению к звезде.

        var sunSystemData = [ 0: "Sun", 1:"Mercury", 2:"Venus", 
	3:"Earth", 4:"Mars", 5:"Jupiter", 6:"Saturn", 7:"Uranus", 8:"Neptune"]

    

Мы не указали типы для словаря явно. Вы можете проверить как их определил Swift, используя функцию стандартной библиотеки Swift (Swift Standard Library) type(of: T).

        type(of: sunSystemData) // Dictionary<Int, String>

    

Обратиться к элементу словаря можно, написав ключ внутри скобок. Результат будет содержать значение опционального типа (Optional), а если по заданному ключу (в скобках) нет соответствующего элемента, то получим nil.

nil в Swift означает отсутствие значения.

Получим нашу родную планету и выведем ее в консоль.

        var homePlanet = sunSystemData[3]
print("My home is \(homePlanet)")

    

Когда мы попытались вывести переменную в консоль, то получили предупреждение!

🪲
String interpolation produces a debug description for an optional value; did you mean to make this explicit?

Давайте посмотрим какой именно тип приняла переменная.

        type(of: homePlanet) // Optional<String>

    

Чтобы избавиться от предупреждения в интерполяции строки, нам необходимо указать что именно мы выводим. Сделать это можно при помощи оператора as. В этом случаи мы увидим напечатанный nil , когда значение отсутствует. Попробуйте обратиться к элементу с ключом 0.

        print("My home is \(sunSystem[0] as String?)") // My home is nil
print("My home is \(homePlanet as String?)") // My home is Earth
    

Другим решением может быть присвоение переменной значения по умолчанию.

Сделать это мы можем при помощи оператора объединения по nil??

        var homePlanet = sunSystem[3] ?? "Unknown planet"
print("My home is \(homePlanet)") // My home is Earth

    

Пока опустим темы об опциональных типах и приведении типов(type casting).

В следующих статьях я расскажу о них подробней.

Продолжим!

Кстати, если вы считаете, что Плутон это планета, то можете добавить её самостоятельно!

        sunSystem[9] = "Pluto"

    

Чем отличаются массивы от словарей?

  • Массивы хранят множество значений определенного типа в упорядоченном списке. Любое значение может присутствовать несколько раз в списке.
  • Словари хранят множество значение определенного типа, где каждое значение связано с уникальным ключом, который идентифицирует это значение внутри словаря. Элементы в словаре не имеют определенного порядка, как в массивах.

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

Например:

        var HierarchyOfNumbers: [Character:String] = [
	"N":"Natural Numbers"
	"Z":"Integers"
	"Q":"Rational Numbers"
	"R":"Real Numbers"
	"C":"Complex Numbers"
]

    

Управление потоком

Часто необходимо выполнять различный код базирующийся на определенных условий (conditions).

  • Если вам необходимо написать различную логику, которая зависит от определенных условий, тогда используйте условные инструкцииif, switch. Инструкцияifхорошо подходит для простых сравнений, и нескольких вариантов исходов.
  • Инструкция switch подходит для более сложных условий. Лучше использовать ее, когда вам необходимо выбрать из множества вариантов альтернатив, основываясь на каком-либо значении, т.е. выполнить код соответствующий определенному шаблону.
  • Когда вам необходимо многократно повторить какие либо инструкции, используйте циклы: for-in,while.

If…else

Инструкция if бывает трех форм.

Традиционное ее представление выглядит так:

        var condition = true
if condition {
	// 
} else {
	//
}

    

В зависимости от условия, мы попадаем в определенные блоки кода. Когда условие истинно(true), выполняется блок следующий за if, а когда ложно(false), выполняется блок следующий за else.

Например, вы решили, что Плутон планета? Сейчас узнаем!

        var isPluto = sunSystemData[9] != nil

if isPluto {
	print("Pluto is planet")
} else {
	print("Pluto is not planet")
}

    

if…

Мы можем опустить блок else, когда нам необходимо выполнить только одно условие.

Рассмотрим такой случай. У нас есть данные, и мы не хотим перезаписать значение. Тогда необходимо проверить наличие данных, и только после добавить значение.

        var key = 0, value = "Sun"
var isExists = sunSystemData[key] != nil // false
if !isExists { // true
	sunSystemData[key] =  value
}

    

if…else if…else

Также мы можем добавить условия следующее за else.

        var value = 0
if value > 0 {
	print("\(value)")
} else if value == 0  {
	print(value)
} else {
	print(value)
}

    

Вы можете комбинировать if else.

Например:

        var a = 0, b = 3, c = 1
if a > b {
	print(a - b)
} else if b > a { 
	print(b - a)
} else if c < a {
	print(a - c)
} else {
	 print(a + b + c)
}

    

switch

В самой простой форме switch выглядит так:

        var value = "R2-D2"
switch value {
	case "R2-D2": print("Take him!")
	case "C-3PO": print("Take him!")
	default: 
		print("These aren't the droids you're looking for.")
}

    

Ключевое слово case определяет возможный случай (шаблон), с которым сравнивается значение value. Затем исполняется соответствующий блок кода на основе первого успешно совпавшего шаблона. В случаи если совпадений не найдено, исполняется блок default , который всегда следует после остальных случаев. Условие default гарантирует что наша конструкция полная и законченная.

Поменяйте значение value, и посмотрите, какой блок кода исполнился.

for-in

Цикл for-in упрощает перебор по последовательностям, т.к. массивы, словари, строки.

Синтаксис for-in выглядит так:

        for value in sequence{
  // 
}

    

Цикл for-in исполняет инструкции определенное количества раз, пока мы не достигнем конца последовательности.

valueэлемент последовательности на каждой итерации(iteration, повторении).

Рассмотрим пример перебора значений массива и словаря.

        for digit in digits  {
	print(digit)
}
for (key, value) in sunSystemData {
	print("Key:\(key) Value:\(value)")
}

    

while

while имеет две формы.

Первая из них начинается с вычисления условия. Такой цикл можно рассматривать как повторяющаяся инструкция if.

        var condition = true;
while (condition) {
	// тело цикла
}

    

Например, мы хотим пройти 10 000 шагов.

        var stepGoal = 10_000
var mySteps = 0
while mySteps < 10_000 {
	mySteps += 1
}

    

Цикл будет работать пока условие mySteps < 10_000 принимает булево значение true.

Внутри тела цикла мы увеличиваем переменную mySteps на один шаг.

Когда условие принимает значение false, то цикл останавливается.

Расчет факториала числа.

        var counter = 5                
var factorial = 1 // начальное значение             

while counter > 0 {            
    factorial *= counter       
    counter -= 1               
}

print(factorial)  

    

Здесь я вам предлагаю разобраться самостоятельно 😉

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

В остальном repeat-while ведет себя идентично while.

        var condition = true
repeat {
	// тело цикла 
} while (condition)

    

Также цикл while используется, когда нам заранее неизвестно число итераций последовательности. В отличии от цикла for-in.

Функции

В Swift есть два типа функций:

Пользовательские(User-defined Function) и функции стандартной библиотеки (Standard Library Functions).

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

Функция – блок кода, который выполняет конкретную задачу.

Каждая функция имеет уникальное имя, которое является ее идентификатором.

        func functionName(parameters) -> returnType {
  // тело функии 
}

    

Объявить функцию можно при помощи ключевого слова func.

За ним следует имя функцииfunctionName.

В скобках указываются параметры(parameters) функции.

Параметр – это значение определенного типа, принимаемое функцией.

returnType – указывает, какого типа значение возвращает функция

Чтобы исполнить функцию, ее необходимо вызвать (как мы уже делали это с print()).

        // вызов функции
functionName()

    

Функции без параметров

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

        func greetings() {
	 print("Hello Proglib!")
}
greetings()

print("Done!")

    

Мы объявили функцию с именем greetings() , которая просто печатает "Hello Proglib!" в консоли. Она не имеет параметров и возвращаемого значения. При вызове функции управление программой переходит к определению функции. Затем исполняется код, который содержится внутри тела функции:

        print("Hello Proglib!")
    

Как только функция завершится, управление потоком переходит к следующей инструкции после вызова функции.

        print("Done!")
    

Функции с параметрами

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

Например, разница между двумя значениями:

        func difference(a: Int, b: Int) -> Int {
	return a - b 
}
var result = difference(a: 3, b: 2)
print(result)

    

Мы объявили функцию с именем difference, которая принимает два параметра типа Int и возвращает значение типа → Int. При вызове функции, мы передаем параметры в круглых скобках (a: 3, b: 2).

Каждый параметр функции состоит из метки аргумента и имени параметра. Имена параметров используются при реализации функции.

        func difference(of a: Int, less b: Int) -> Int {
  return a - b
}

    

Метка аргумента используется при вызове функции.

        difference(of: 1, less: 2)
    

По умолчанию параметры используют свое имя параметра в качестве метки аргумента(как в первом примере).

Если вам не нужна метка для аргумента, то напишите _ вместо конкретного названия метки для аргумента.

        func difference(_ x: Int, _ y: Int) -> Int {
  return x - y
}

difference(3, 4)

    

Мы можем опустить оператор return , когда тело функции является одним выражением.

        func difference(a: Int, b: Int) -> Int {
	a - b 
}

    

Заключение

Мы познакомились с фундаментальным синтаксисом языка программирования Swift. Рассмотрели основные операторы, научились объявлять переменные и объединять код в функции. В следующие полчаса разберем каждую тему более глубоко. На сегодня все! Playground доступен на Github.

04
Ноя
2021

🍏 Самоучитель для начинающих: освойте основы Swift за 30 минут

Думаете, чтобы освоить Swift нужно много времени? И да, и нет. В изучении языков программирования важна практика, но что может сделать начинающий за 30 минут? Только познакомиться с базовым синтаксисом с помощью нашего самоучителя.

Создать Playground

Запустим Xcode и создадим Playground. Назовем его “BasicSwift”:

File → New → Playground…

⌥⇧⌘N

Swift Playground – это такой тип файлов, который представляет из себя игровую площадку для ваших экспериментов с кодом Swift. Результат исполнения каждой строчки кода вы можете наблюдать в боковой панели (Sidebar).

Переменные, константы, типы

Переменные обозначаются с помощью ключевого слова var

        var hello = "Hello Proglib"
    

Чтобы объявить константу используется ключевое слово let

        let brandName = "Proglib"
    
Swift – это статистический типизированный язык, а это значит, что мы должно явно задать тип каждой переменной во время компиляции.

Указывать тип переменной необязательно. Компилятор Swift автоматически его определит. Эта особенность называется интерференцией типов(type inference).

Swift предлагает следующие базовые типы: Int или UInt, Double, Float, String, Character, Bool, Tuples, Optional.

Множество типов фундаментально и чаще всего используется для объявления переменных. Забегая вперед скажу, что вы также можете создавать и свои собственные типы.

Мы не будет детально разбирать их все! Посвятим этой теме следующую 30-ти минутку.

Укажем тип нашей константы.

        let brandName: String = "Proglib"
    
Больше полезной информации вы можете найти на нашем телеграм-канале «Библиотека мобильного разработчика».

Интерполяция строк

Давайте теперь выведем имя бренда в консоль при помощи функции print().

        print(brandName)
    

Интерполяция – это способ объединить переменные и константы внутри строки.

Теперь присвоим переменной hello новое значение, и выведем в консоль.

        hello = "Hello \(brandName)"
print(hello)
    

Вот еще пример:

        let name = "Ivan" 
var age: UInt8 = 30
print("\(hello). My name is \(name). I am \(age)")

    

Базовые операторы

Здесь все интуитивно. Предлагаю вам поэкспериментировать с ними самостоятельно и посмотреть на результаты.

Арифметические операторы

+ сложение
вычитание
/ деление
% деление по модулю
        var a = 1, b = 2
var result = a + b
// etc

    

Операторы присвоения

= , +=, -=, /=, %=

        var h = 10
h += 1
h /= 2
h %= 2
// etc

    

Операторы сравнения

Операторы сравнения в качестве результата возвращают значения типа Bool

==, ≠, >, <, ≥, ≤

        let condition = 2 > 3
let condition2 = 1 != 0
let resultCondition = condition == condition2
// etc

    

Логические операторы

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

Рассмотрим на примере:

        // логическое И
print(true && true)      // true
print(true && false)     // false

// логическое ИЛИ
print(true || false)      // true

// логическое НЕ – инвертирует булево значение
print(!true)              // false

    

Это еще не все операторы, которые существуют в Swift. Остальные мы рассмотрим в контексте целей их предназначения.

Коллекции

Swift предлагает нам три основных типа коллекций: Array(Массивы), Dictionary(Словари) и Set(Множества). Сейчас мы рассмотрим первые два.

Array

Используйте квадратные скобки [], чтобы создавать массивы(Array) и словари(Dictionary).

Получить доступ к элементам массива можно написав индекс внутри скобок.

        let digits: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9]
 print(digits[3])

    

Попробуйте изменить значение в массиве digits. Что произойдет?

        digits[0] = 1
    
🪲
Cannot assign through subscript: ‘digits’ is a ‘let’ constant

Поскольку мы объявили массив как константу, то и все его значения становится не изменчивыми (immutable), как и его размер. А если мы объявим массив как переменную, то он будет изменчивым (mutable). Такое свойство называют изменчивостью коллекций (mutability of collection).

Это утверждение справедливо и для Словарей.

        // mutable
var numbers = [50, 10, 20, 34, 45]
print(numbers[0])
numbers[0] = numbers[0] + 1

    

Dictionary<Key, Value>

Создадим словарь sunSystemData, в который добавим данные о солнечной системе.

Пусть ключами (key) данного словаря будут номера объектов солнечной системы, а значениями (value) названия объектов. Поскольку звезда рождается первой, то будем считать, что ключ со значением 0 всегда указывает на звезду. Остальные объекты следуют в порядке по отношению к звезде.

        var sunSystemData = [ 0: "Sun", 1:"Mercury", 2:"Venus", 
	3:"Earth", 4:"Mars", 5:"Jupiter", 6:"Saturn", 7:"Uranus", 8:"Neptune"]

    

Мы не указали типы для словаря явно. Вы можете проверить как их определил Swift, используя функцию стандартной библиотеки Swift (Swift Standard Library) type(of: T).

        type(of: sunSystemData) // Dictionary<Int, String>

    

Обратиться к элементу словаря можно, написав ключ внутри скобок. Результат будет содержать значение опционального типа (Optional), а если по заданному ключу (в скобках) нет соответствующего элемента, то получим nil.

nil в Swift означает отсутствие значения.

Получим нашу родную планету и выведем ее в консоль.

        var homePlanet = sunSystemData[3]
print("My home is \(homePlanet)")

    

Когда мы попытались вывести переменную в консоль, то получили предупреждение!

🪲
String interpolation produces a debug description for an optional value; did you mean to make this explicit?

Давайте посмотрим какой именно тип приняла переменная.

        type(of: homePlanet) // Optional<String>

    

Чтобы избавиться от предупреждения в интерполяции строки, нам необходимо указать что именно мы выводим. Сделать это можно при помощи оператора as. В этом случаи мы увидим напечатанный nil , когда значение отсутствует. Попробуйте обратиться к элементу с ключом 0.

        print("My home is \(sunSystem[0] as String?)") // My home is nil
print("My home is \(homePlanet as String?)") // My home is Earth
    

Другим решением может быть присвоение переменной значения по умолчанию.

Сделать это мы можем при помощи оператора объединения по nil??

        var homePlanet = sunSystem[3] ?? "Unknown planet"
print("My home is \(homePlanet)") // My home is Earth

    

Пока опустим темы об опциональных типах и приведении типов(type casting).

В следующих статьях я расскажу о них подробней.

Продолжим!

Кстати, если вы считаете, что Плутон это планета, то можете добавить её самостоятельно!

        sunSystem[9] = "Pluto"

    

Чем отличаются массивы от словарей?

  • Массивы хранят множество значений определенного типа в упорядоченном списке. Любое значение может присутствовать несколько раз в списке.
  • Словари хранят множество значение определенного типа, где каждое значение связано с уникальным ключом, который идентифицирует это значение внутри словаря. Элементы в словаре не имеют определенного порядка, как в массивах.

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

Например:

        var HierarchyOfNumbers: [Character:String] = [
	"N":"Natural Numbers"
	"Z":"Integers"
	"Q":"Rational Numbers"
	"R":"Real Numbers"
	"C":"Complex Numbers"
]

    

Управление потоком

Часто необходимо выполнять различный код базирующийся на определенных условий (conditions).

  • Если вам необходимо написать различную логику, которая зависит от определенных условий, тогда используйте условные инструкцииif, switch. Инструкцияifхорошо подходит для простых сравнений, и нескольких вариантов исходов.
  • Инструкция switch подходит для более сложных условий. Лучше использовать ее, когда вам необходимо выбрать из множества вариантов альтернатив, основываясь на каком-либо значении, т.е. выполнить код соответствующий определенному шаблону.
  • Когда вам необходимо многократно повторить какие либо инструкции, используйте циклы: for-in,while.

If…else

Инструкция if бывает трех форм.

Традиционное ее представление выглядит так:

        var condition = true
if condition {
	// 
} else {
	//
}

    

В зависимости от условия, мы попадаем в определенные блоки кода. Когда условие истинно(true), выполняется блок следующий за if, а когда ложно(false), выполняется блок следующий за else.

Например, вы решили, что Плутон планета? Сейчас узнаем!

        var isPluto = sunSystemData[9] != nil

if isPluto {
	print("Pluto is planet")
} else {
	print("Pluto is not planet")
}

    

if…

Мы можем опустить блок else, когда нам необходимо выполнить только одно условие.

Рассмотрим такой случай. У нас есть данные, и мы не хотим перезаписать значение. Тогда необходимо проверить наличие данных, и только после добавить значение.

        var key = 0, value = "Sun"
var isExists = sunSystemData[key] != nil // false
if !isExists { // true
	sunSystemData[key] =  value
}

    

if…else if…else

Также мы можем добавить условия следующее за else.

        var value = 0
if value > 0 {
	print("\(value)")
} else if value == 0  {
	print(value)
} else {
	print(value)
}

    

Вы можете комбинировать if else.

Например:

        var a = 0, b = 3, c = 1
if a > b {
	print(a - b)
} else if b > a { 
	print(b - a)
} else if c < a {
	print(a - c)
} else {
	 print(a + b + c)
}

    

switch

В самой простой форме switch выглядит так:

        var value = "R2-D2"
switch value {
	case "R2-D2": print("Take him!")
	case "C-3PO": print("Take him!")
	default: 
		print("These aren't the droids you're looking for.")
}

    

Ключевое слово case определяет возможный случай (шаблон), с которым сравнивается значение value. Затем исполняется соответствующий блок кода на основе первого успешно совпавшего шаблона. В случаи если совпадений не найдено, исполняется блок default , который всегда следует после остальных случаев. Условие default гарантирует что наша конструкция полная и законченная.

Поменяйте значение value, и посмотрите, какой блок кода исполнился.

for-in

Цикл for-in упрощает перебор по последовательностям, т.к. массивы, словари, строки.

Синтаксис for-in выглядит так:

        for value in sequence{
  // 
}

    

Цикл for-in исполняет инструкции определенное количества раз, пока мы не достигнем конца последовательности.

valueэлемент последовательности на каждой итерации(iteration, повторении).

Рассмотрим пример перебора значений массива и словаря.

        for digit in digits  {
	print(digit)
}
for (key, value) in sunSystemData {
	print("Key:\(key) Value:\(value)")
}

    

while

while имеет две формы.

Первая из них начинается с вычисления условия. Такой цикл можно рассматривать как повторяющаяся инструкция if.

        var condition = true;
while (condition) {
	// тело цикла
}

    

Например, мы хотим пройти 10 000 шагов.

        var stepGoal = 10_000
var mySteps = 0
while mySteps < 10_000 {
	mySteps += 1
}

    

Цикл будет работать пока условие mySteps < 10_000 принимает булево значение true.

Внутри тела цикла мы увеличиваем переменную mySteps на один шаг.

Когда условие принимает значение false, то цикл останавливается.

Расчет факториала числа.

        var counter = 5                
var factorial = 1 // начальное значение             

while counter > 0 {            
    factorial *= counter       
    counter -= 1               
}

print(factorial)  

    

Здесь я вам предлагаю разобраться самостоятельно 😉

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

В остальном repeat-while ведет себя идентично while.

        var condition = true
repeat {
	// тело цикла 
} while (condition)

    

Также цикл while используется, когда нам заранее неизвестно число итераций последовательности. В отличии от цикла for-in.

Функции

В Swift есть два типа функций:

Пользовательские(User-defined Function) и функции стандартной библиотеки (Standard Library Functions).

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

Функция – блок кода, который выполняет конкретную задачу.

Каждая функция имеет уникальное имя, которое является ее идентификатором.

        func functionName(parameters) -> returnType {
  // тело функии 
}

    

Объявить функцию можно при помощи ключевого слова func.

За ним следует имя функцииfunctionName.

В скобках указываются параметры(parameters) функции.

Параметр – это значение определенного типа, принимаемое функцией.

returnType – указывает, какого типа значение возвращает функция

Чтобы исполнить функцию, ее необходимо вызвать (как мы уже делали это с print()).

        // вызов функции
functionName()

    

Функции без параметров

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

        func greetings() {
	 print("Hello Proglib!")
}
greetings()

print("Done!")

    

Мы объявили функцию с именем greetings() , которая просто печатает "Hello Proglib!" в консоли. Она не имеет параметров и возвращаемого значения. При вызове функции управление программой переходит к определению функции. Затем исполняется код, который содержится внутри тела функции:

        print("Hello Proglib!")
    

Как только функция завершится, управление потоком переходит к следующей инструкции после вызова функции.

        print("Done!")
    

Функции с параметрами

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

Например, разница между двумя значениями:

        func difference(a: Int, b: Int) -> Int {
	return a - b 
}
var result = difference(a: 3, b: 2)
print(result)

    

Мы объявили функцию с именем difference, которая принимает два параметра типа Int и возвращает значение типа → Int. При вызове функции, мы передаем параметры в круглых скобках (a: 3, b: 2).

Каждый параметр функции состоит из метки аргумента и имени параметра. Имена параметров используются при реализации функции.

        func difference(of a: Int, less b: Int) -> Int {
  return a - b
}

    

Метка аргумента используется при вызове функции.

        difference(of: 1, less: 2)
    

По умолчанию параметры используют свое имя параметра в качестве метки аргумента(как в первом примере).

Если вам не нужна метка для аргумента, то напишите _ вместо конкретного названия метки для аргумента.

        func difference(_ x: Int, _ y: Int) -> Int {
  return x - y
}

difference(3, 4)

    

Мы можем опустить оператор return , когда тело функции является одним выражением.

        func difference(a: Int, b: Int) -> Int {
	a - b 
}

    

Заключение

Мы познакомились с фундаментальным синтаксисом языка программирования Swift. Рассмотрели основные операторы, научились объявлять переменные и объединять код в функции. В следующие полчаса разберем каждую тему более глубоко. На сегодня все! Playground доступен на Github.

31
Окт
2021

Как Facebook идентифицирует приложение?

Всем привет. В java и react native нулевые скилы, пытаюсь для маркетологов разобраться в одной проблеме. Если кто знаком с подключением приложений в рекламные кабинеты, расскажите пожалуйста как FB идентифицирует приложение ? Есть какой-ни…

24
Окт
2021

SVG Text Trace по-другому отображается на устройствах iOS

Я использую анимацию трассировки SVG с помощью CSS, меняя значения stroke-dasdoffset с помощью анимации ключевых кадров.
При тестировании моих проектов на устройствах iOS (iPad / iPhone) кажется, что stroke-dasdoffset / массив рассчитывает…

28
Сен
2021

🍏 Взаимодействие SwiftUI с вебом. Часть 3: JavaScript – инструкция для новичков

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

Мы уже добавили в проект (код доступен на GitHub – прим. ред.) перечисление WebViewNavigationAction, которое описывает три действия: назад, вперед, перезагрузить, а затем разобрались с пользовательским интерфейсом приложения, навигацией и получением информации с веб-страницы в Swift. Сегодня более подробно рассмотрим работу с JavaScript.

Создаем local.html

Подготовим простой файл HTML и отобразим его в WebView.

Создадим папку www в проекте. Внутрь положим файл local.html со следующим содержимым:

local.html
        <!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta http-equiv="Content-Style-Type" content="text/css">
  <title>Local HTML</title>
  <style>
    body {
      background: -webkit-linear-gradient(bottom left, #2b2f44 0%,#312442 100%);
      text-align: center;
      font-family: monospace;
      font-weight: bold;
      font-size: large;
    }
    .title {
      color: #FFFFFF;
    }
    .container {
      display: grid;
      justify-content: center;
      align-content: center;
      grid-auto-flow: row;
      gap: 12px;
    }
    .item {
      max-width: 600px;
      padding: 12px;
      background: #000000;
      border-radius: 5px;
      color: #FFFFFF;
    }
    #ok {
      background-color: #000000;
      color: #FFFFFF;
      font-size: medium;
      padding: 12px;
    }
  </style>
</head>
<body>
  <header>
    <h1 class="title">Interaction with JavaScript</h1>
  </header>
    <div class="container">
      <div class="item">
        <h1>Swift</h1>
      </div>
      <div class="item">
        <h1>JavaScript</h1>
      </div>
      <button id="ok">OK</button>
    </div>
</body>
</html>
    

При добавлении не забудьте указать create folder reference.

Чтобы показать локальный файл HTML, нам необходимо доработать WebView, но для начала обновим конфигурацию WebView в ContentView:

        WebView(type: .local, url: "local", viewModel: viewModel)
    

Вернемся в WebView и обновим логику в updateUIView:

        func updateUIView(_ webView: WKWebView, context: Context) {
	if let urlValue = url  {
		if type == .local {
			if let localUrl = Bundle.main.url(forResource: urlValue, withExtension: "html", subdirectory: "www") {
			webView.loadFileURL(localUrl, allowingReadAccessTo: localUrl.deletingLastPathComponent())
				}
			} else if type == .public {
				if let requestUrl = URL(string: urlValue) {
					webView.load(URLRequest(url: requestUrl))
				}
			}
		}
	}
    

Обратите внимание, для загрузки локального файла мы используем следующий метод:

        func loadFileURL(_ URL: URL, allowingReadAccessTo readAccessURL: URL) -> WKNavigation?
    
Важный момент!
Если readAccessURL ссылается на один файл, то Webkit сможет загрузить только его; если это директория, то и файлы внутри каталога могут быть загружены WebKit. В данный момент мы загружаем только один файл и все описываем внутри него (стили, скрипты).

Протестируем что у нас получилось!

А зачем оно все?

На практике встречаются такие задачи, когда нам нужно подгрузить веб-страницу с определенным контентом. Иногда мы хотим показать только часть содержимого и скрыть, например, <header> веб-страницы, который добавляет избыточную навигацию в приложение. В других случаях нам нужно понимать, что пользователь нажал на кнопку OK (когда это событие произошло).

Отладка JavaScript
Далее мы рассмотрим как работать с JavaScript из Swift, и наоборот. Если вы еще не знакомы с Web технологиями, такими как JavaScript, HTML, CSS, то ничего страшного. Я расскажу теоретический минимум, которые необходимо знать. Прежде чем добавлять сценарии JavaScript в приложение, я рекомендую проверить их в браузере. Откройте инструменты разработчика и протестируйте код в консоли.

Когда вы будете запускать код в WKWebView, global scope (глобальная область видимости) может стать проблемой, которая приводит к краху приложения из-за ошибок повторного объявления переменных и т.п.

Пара слов о global scope

В JavaScript используют const для объявления констант и let – для обычных переменных. Вы также можете воспользоваться var, но здесь тоже есть некоторые особенности, которые могут привести в замешательство.

Поэкспериментируйте с объявлением переменных. Например, получите заголовок всей веб-страницы document.title, как мы это уже делали в прошлой статье. Объявите переменные с const, let.

Если вы выполните в консоли несколько раз const title = document.title, то получите ошибку. Это можно исправить, если заменить const на let или ввести локальную область видимости (local scope), заключив код в фигурные скобки:

        { const title  = document.title; }
    

Чтобы проблем, стоит оборачивать код в функции. Поскольку они дают нам дополнительный контекст, вы можете быть уверены, что не получите подобных ошибок.

        function getDocTittle() { 
	const title  = document.title; 
	console.log(title);
}
getDocTitle();
    
Если вы хотите разобраться, как работают переменные и области видимости (scope), прочтите этот пост.

Селекторы

Чтобы произвести какие-либо действия с элементами веб-страницы, нужно получить на них ссылку. Selectors API предоставляет нам простой и эффективный способ получить элемент из DOM (Document-Object-Model), сопоставляя элементы заданному в параметре множеству селекторов. Спецификация добавляет два метода, которые мы можем применять к документу (Document), элементу (Element), и фрагменту документа (DocumentFragment):

  • querySelector(selectors) вернет первое совпадение. Если совпадений не найдено, то null.
  • querySelectorAll(selectors) вернет все совпадения (NodeList), или пустой NodeList, если их нет.

Когда сайты имеют хорошую разметку, чаще всего в качестве параметра (selectors) мы передаем CSS-класс, ID элемента, имя тега элемента. Для для формирования более точных запросов следует передавать множество CSS-селекторов.

Рассмотрим все вышеизложенное на примерах.

Например, в <body> на веб-странице у нас есть:

  • <header> внутри которого заголовок.
  • <h1> c классом title.
  • <div> – блок с классом container, в котором содержатся:
  • два блока <div> с классом item, в котором содержатся:
  • заголовки <h1>;
  • кнопка <button> с идентификатором ok.

Получим наш заголовок из тега <head>:

        const h1 = document.querySelector('h1');
    
Поскольку в нашем примере заголовок в документе самый первый, то мы получили нужное. А если в <head> определили два заголовка? Например, мог быть заголовок в шапке сайта, который описывает бренд, а за ним следует заголовок, который задает название контенту содержимого. В таком случае нам бы пришлось создать более точный запрос.

Классы более общие, а идентификаторы всегда уникальные во всем документе, и у каждого элемента в HTML есть глобальные атрибуты.

Атрибут class – один из них. Он позволяет CSS и JavaScript выбрать и получить элемент при помощи селекторов CSS или функций JavaScript.

Если хотим получить именно название (title), то лучше воспользоваться более точным запросом. В передаваемом селекторе, точка означает, что мы имеем дело с именем класса.

        const title = document.querySelector('.title');
    

Оба запроса вернули один и тот же результат:

        <h1 class="title">Interaction with JavaScript</h1>
    

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

Теперь получим кнопку с идентификатором ok:

        const button = document.querySelector('#ok');
    

В передаваемом селекторе решетка означает, что это имя идентификатора.

Результат:

        <button id="ok">OK</button>
    

Воспользуемся следующим запросом, чтобы исследовать элементы тега div:

        let divs =  document.querySelectorAll("div")


    

В результате получим состоящий из трех элементов NodeList:

        [div.container, div.item, div.item]


    

Создадим более точные запросы, чтобы получить контент заголовков <h1> внутри элементов div.item:

         let itemsContent = document.querySelectorAll("div.item h1");


    

C помощью метода forEach мы можем перебирать элементы NodeList. Выведем результат в консоль:

        itemsContent.forEach(function(title) { console.log(title.innerText)) }
    
Управление элементами
Как получить элементы из документа, мы уже разобрались. Давайте научимся ими манипулировать. Стоить отметить, что существует несколько способов как и получения элементов, так и несколько способов выполнить ту или иную задачу с некоторыми тонкостями и особенностями. Например, такие задачи как, скрытия элементов, проверки на существование элемента в документе и т.д. Поэтому если вы уже подкованы в этой области, поделитесь своим опытом в комментариях. Далее мы не будем останавливаться на всевозможных способах и деталях. Просто посмотрим на часто распространенные примеры.

Скрытие элементов

Например, чтобы скрыть title, воспользуемся свойством display:

        title.style.display = 'none';
    

Разумеется, при обновлении WebView элемент снова появится.

Существует ли элемент?

Часто встречающаяся задача – проверка присутствия элемента. Если вы не совсем уверены в получаемом результате, оберните его в условный оператор if. Попробуем получить первый заголовок второго уровня <h2>, которого не существует в примере, и скрыть его:

        const h2 = document.querySelector('h2');
if(h2) { h2.style.display = 'none'; }
    

Клики и события

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

JavaScript предоставляет нам метод addEventListener, в котором два параметра:

  • название события, например, ‘click’.
  • функция, которая выполняет какой-либо код, когда происходит событие.
        button.addEventListener('click', function(e) { 
  console.log(e); 
  console.log('OK Clicked');
});
    

Мы добавили событие click, и по нажатию на кнопку OK должны увидеть в консоли: ”OK Clicked”.

Параметр е, который передается в функцию, описывает само событие click. К самому элементу можно обратиться через e.target.

Стоить отметить, что в JavaScript есть стрелочные функции, однако если вы незнакомы с особенностями языка, это может вызвать замешательство. Чтобы избежать путаницы с тем, куда указывает this, я использовал обычную функцию, где this в теле функции указывает на саму кнопку.

Как получить контент элемента или атрибута?

Существует несколько способов в DOM API получить контент элемента, но у каждого свои особенности и назначения. Допустим мы хотим получить контент нашего названия title.

Например, при помощи свойств:

  • textContent
  • innerText

title.innerText и title.textContent в данном случае возвращают одни и те же результаты. Однако они весьма отличаются. textContent может вернуть нам даже текстовое содержимое блока <style>.

Поскольку чаще всего нам требуется получить читабельный контент, то innerText является лучшим выбором и используется в большинстве случаев, но его результат зависит от стилей. Если элемент скрыт, то мы ничего не получим.

Чтобы разница была явно видна, получите элемент HTML и посмотрите, что будет в этих свойствах.

Для более подробного изучения смотрите документацию, а также обратите внимание на свойства outerText, innerHtml и outerHtml.

Порой нам необходимо вытащить у элемента <img> ссылку на изображение, которая находится в атрибуте src или значение элемента <input>. Сделать это довольно просто:

        let imgSrc = img.src;
    
        const input = document.querySelector('#mylInput');
let inputValue = input.value
    

Run JS! Run!

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

        open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)


    
В этот метод мы передаем строку, которая представляет из себя код на JavaScript и получаем результат. Если мы ничего не возвращаем явно, то JavaScript runtime вернет нам undefined. Для отслеживания, каких либо блоков кода на JavaScript удобно использовать в качестве возвращаемого результата true или false. В completionHandler передается результат исполнения скрипта или ошибка.

В документации сказано, что вызов этого метода равноценен вызову метода, где значение framenil представляет главный фрейм, а значение contentWorldWKContentWorld.pageWorld:

        public func evaluateJavaScript(_ javaScript: String, in frame: WKFrameInfo? = nil, in contentWorld: WKContentWorld, completionHandler: ((Result<Any, Error>) -> Void)? = nil)


    

Данное улучшение доступно начиная с iOS 14.

  • WKFrameInfo содержит информацию о фрейме на странице.
  • WKContentWorld – этот объект определяет область видимости выполнения кода JavaScript, т.е. мы должны использовать его как пространство имен (namespace).
Рассмотрим его более подробно. Это ключевой параметр функции, который дает нам большую гибкость и обеспечивает предотвращение многих конфликтов. Мы можем его использовать для предотвращения конфликтов между скриптами на веб-странице. Выполнение сценарии JavaScript в собственном WKContentWorld дает нам отдельную копию переменных среды для изменения.

Получим заголовок из шапки и покажем его в нашем NavigationView. Для этого создадим файл JSUserScripts.swift, где напишем сценарии JavaScript, которые будут модифицировать страницу и забирать из нее данные.

Напишем две функции для получения и скрытия заголовка на веб-странице:

JSUserScripts.swift
        let getHeaderTitle  = """
function getHeaderTitle() {
	const headerTitle = document.querySelector('h1.title');
	return headerTitle.innerText;
}
getHeaderTitle();
"""

let hideHeaderTitle = """
function hideHeaderTitle() {
	const headerTitle = document.querySelector('h1.title');
	headerTitle.style.display = 'none';
}
hideHeaderTitle();
"""
    

Теперь заменим старый метод evaluateJavaScript улучшенной версией:

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
	print("didFinish")
	self.parent.viewModel.isLoaderVisible.send(false)
			
	webView.evaluateJavaScript(getHeaderTitle, in: nil, in: .defaultClient) { result in
		switch result {
			case .success(let value):
				if let title = value as? String {
					self.parent.viewModel.webTitle.send(title)
				}
						
			case .failure(let error):
				print(error.localizedDescription)
		}
	}
			
	webView.evaluateJavaScript(hideHeaderTitle, in: nil, in: .defaultClient)
}

    

Свойство .defaultClient предоставляет пространство имен (namespace) по умолчанию для приложения.

Если нам нужно запустить скрипт внутри пространства имен текущей веб-страницы, мы можем использовать .page

        webView.evaluateJavaScript(hideHeaderTitle, in: nil, in: .page)

    

Также мы можем создавать собственные пространства имен:

        webView.evaluateJavaScript(hideHeaderTitle, in: nil, in: .world(name: "magic"))


    

В нашем примере это излишество, но если бы мы создавали веб-браузер с расширениями на JavaScript, пришлось бы создавать уникальное пространство имен для каждого расширения.

Еще один интересный метод, который пришел вместе с iOS 14:

         public func callAsyncJavaScript(_ functionBody: String, arguments: [String : Any] = [:], in frame: WKFrameInfo? = nil, in contentWorld: WKContentWorld, completionHandler: ((Result<Any, Error>) -> Void)? = nil)
    

В параметр functionBody, в качестве строки мы передаем само тело функции, а в качестве arguments мы можем передавать переменные, которые будут использованы в функции, что очень удобно. Такая гибкость позволяет нам переиспользовать блоки кода JavaScript.

Как видно из названия, Async означает, что строка будет запущена как асинхронная функция. В этом случае можно использовать await и работать с JavaScript-обещаниями (Promise). Такая техника необходима, когда мы не знаем, когда именно будет завершена работа функции или когда необходимо что-то запустить через определенное время (например setTimeout).

Рассмотрим несколько примеров:

        let hideAnyElement  = """
	const element = document.querySelector(selector);
	element.style.display = 'none';
"""

let getElementInnerText = """
	const element = document.querySelector(selector);
	return element.innerText;
"""
    

Исполним JavaScript:

        webView.callAsyncJavaScript(hideAnyElement, arguments: ["selector":"#ok"], in: nil, in: .defaultClient)

    

Код стал элегантней. Теперь мы можем передавать селекторы в качестве аргументов и скрывать любые элементы веб-страницы.

Напишем простое обещание (promise):

        let setTimeoutFor = """

	const myPromise = new Promise((resolve, reject) => {
        window.setTimeout(function (){
		  resolve('foo');
		}, timeout);
	  });

	
	 await myPromise;
	 return myPromise;
"""
    

Исполним JavaScript:

        let timeout = 3000;
webView.callAsyncJavaScript(setTimeoutFor, arguments: [ "timeout":"\(timeout)"], in: nil, in: .defaultClient) { (result) in
				
				switch result {
				case .success(let response):
					print("Done...");
					print(response);
				case .failure(let error):
					print("Error...");
					print(error)
				}
			}

    

После трех секунд ожидания мы получим результат “foo” в приложении.

Когда мы разобрались с исполнением сценариев JavaScript, инициируя вызов из Swift, рассмотрим и обратный процесс – как выполнить код Swift при помощи JavaScript.

Такую коммуникацию нам помогает реализовать протокол WKScriptMessageHandler и объект WKUserContentController, который предоставляет возможность отправки сообщений JavaScript в WKWebView. Протокол WKScriptMessageHandler определяет всего одну функцию, в которой мы можем получить сообщение из запущенных на веб-странице сценариев.

Напишем расширение для Coordinator, в которое и добавим реализацию этой функции:

        extension WebView.Coordinator: WKScriptMessageHandler {
	func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
		if message.name == "messageAppHandler" {
			if let body = message.body as? String {
				print("Message body: \(body)")
			}
		}
	}
}

    

В методе makeUIView (здесь мы создаем WebView и задаем его конфигурацию) укажем наш класс, который реализует обработчик сообщений и его имя:

        webView.configuration.userContentController.add(context.coordinator, contentWorld: .page, name: "messageAppHandler")

    
  • Аргумент name определяет название нашего обработчика сообщений WKScriptMessageHandler. Обратите внимание, что namе (имя обработчика сообщений) используется для определения, какой именно WKUserContentController отправляет сообщение.
  • Свойство body содержит наше сообщение (строку или объект JSON).
  • На веб-странице, чтобы отправить сообщение нашему обработчику, мы должны вызвать у него метод postMessage.

Добавим скрипт на веб-страницу, и отправим сообщение:

        <script type="text/javascript">
	  sendValueToApp();
	  
	  function sendValueToApp() {
		  window.webkit.messageHandlers.messageAppHandler.postMessage('Hello from web page');
	  }
  </script>
    

Запустим и проверим, что получаем наше сообщение с веб-страницы в консоли.

На этом пока все. Код проекта доступен на Github.

P.S. Happy Code!

14
Авг
2021

Вопрос: как сделать игру/программу с загружаемым кешем [закрыт]

Возможно ли сделать такое, что:
на андроид или iOS будет установлено приложение, которое в зависимости от того, появилась ли новая версия, будет закачивать кеш и использовать его, но не будет обновлять саму программу?
Я такое видел уже во …

21
Июл
2021

🍏 Разработка мобильных игр для iOS: с чего начать, что изучать и какие инструменты использовать

Платформа Apple пользуется огромной популярностью – это привлекает начинающих разработчиков мобильных игр. Разбираемся, какие инструменты придется изучить новичку, чтобы создать очередной хит для AppStore.

13
Июл
2021

🍏 Изучение iOS-разработки в 2021 году: 5 советов новичкам

Если вы пытаетесь изучить разработку мобильных приложений для iOS с нуля, в куче доступных гайдов и дорожных карт легко потеряться. В небольшом обзоре мы собрали необходимый минимум навыков для старта в профессии. Правильный подход – не пыт…

13
Июл
2021

🍏 Изучение iOS-разработки в 2021 году: 5 советов новичкам

Если вы пытаетесь изучить разработку мобильных приложений для iOS с нуля, в куче доступных гайдов и дорожных карт легко потеряться. В небольшом обзоре мы собрали необходимый минимум навыков для старта в профессии. Правильный подход – не пыт…

02
Июл
2021

🍏 Путь в профессию: интервью с iOS-разработчиком Кевином Ренскерсом

Создание приложений для iOS – одно из самых перспективных направлений в IT. Наш корреспондент пообщался с программистом-фрилансером из Голландии, который трудится в этой сфере уже 11 лет. Он рассказал о своем пути в iOS, Swift и SwiftUI, а также о будущем iOS-разработки.

– Как вы пришли к разработке приложений для iOS?

В 2009 году я начал работать в местной компании в качестве программиста на Python. Мы создавали веб-сайты для крупных компаний, используя Plone и Django. В 2010 году некоторые клиенты начали просить приложения для iPhone, и это стало началом моей карьеры разработчика iOS: мне платили за то, чтобы научиться создавать эти приложения.

К тому времени у меня уже был десятилетний опыт работы в программировании. Я начал в 2000 году создавать веб-сайты с помощью HTML и Flash, в 2001 году занялся PHP и JavaScript, а в 2009 году перешел на Python.


– Вы занялись разработкой для iOS в 2010 году. Что изменилось сейчас?

Почти все. Когда я начинал, мы не только все еще использовали Objective-C (который мне действительно очень нравился), но даже ARC еще не существовало. Xcode еще не был интегрированным инструментом, Interface Builder был отдельным приложением, поэтому вы могли открывать обе программы одновременно. Это было очень мило.


Даже блоки (замыкания, обработчики завершения) еще не существовали, не было дополнительных элементов, не существовали литералы массивов и словарей и раскадровки – список можно продолжать и продолжать.

– 11 лет – большой срок. Чему вам пришлось научиться за это время?

Очевидно, мне сначала нужно было изучить Objective-C, UIKit, Foundation и другие фреймворки. Позже мне пришлось изучить Swift, функциональное программирование, реактивное программирование, а также все новые фреймворки и API, которые Apple представила за эти годы. MVC, MVVM, координаторы, составная архитектура – обучение никогда не прекращается!

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

– В 2010 году Apple была не такой огромной корпорацией. Почему вы выбрали разработку под iOS?

Я бы не сказал, что они не были крупной компанией в 2010 году. Конечно, сейчас они огромны, но iPhone стал хитом с самого первого выпуска. В любом случае меня не волновал размер, я просто знал с первых же слухов о телефоне Apple, что он будет отличным. Когда Джобс представил iPhone, я был влюблен и знал, что мне нужен именно такой. Требовалось, чтобы iPhone 3GS был доступен в Нидерландах.

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


Что вы думаете о Swift и SwiftUI? Хотите что-то в них изменить?

Обожаю Свифт! Я дождался Swift 3, пока не начал его использовать. Я работал над большими сложными приложениями Objective-C, мне очень нравился Objective-C, поэтому я дождался подходящего момента для перехода. Этот момент наступил в декабре 2016 года: я только начал работать фрилансером и начал новый проект с нуля, поэтому в тот момент Swift казался очевидным выбором.

SwiftUI, с другой стороны… Мне нравится его концепция, я бы хотел иметь возможность использовать его, но он еще не готов. Я создал побочный проект средней сложности с использованием SwiftUI 1, когда он был только что выпущен, и хотя мне понравилось первоначальное повышение производительности, это быстро превратилось в раздражение, так как я потратил слишком много времени, работая над ошибками и сбоями в самом SwiftUI. Легкие вещи стали действительно легкими, а сложные – невозможными. Я отказался от этого побочного проекта.

Я действительно надеюсь, что SwiftUI стал лучше с iOS 15, но если вы не пишете приложение только для iOS 15+, это не имеет значения. На самом деле я не думаю, что буду использовать SwiftUI для производства в ближайшие 2 или 3 года.

Я бы хотел изменить то, чтобы изменения Swift и SwiftUI можно было использовать со старыми версиями iOS. Например, новая поддержка async/await в Swift действительно потрясающая. Но… только для iOS 15+. Я бы хотел, чтобы Apple нашла способ удалить такие зависимости из самой базовой ОС, чтобы отдельные фреймворки могли обновляться без полного обновления ОС. То же самое и с такими приложениями, как Mail, Weather, Notes и т. д. Почему они являются частью самой iOS? Если бы они были в App Store, как и любое другое приложение, их можно было бы обновлять чаще и с меньшими проблемами.

– Какие ресурсы вы используете для работы и обучения?

– Были у вас ошибки, и что бы вы посоветовали ребятам из России, которые только начинают осваивать профессию?

На самом деле это не ошибка, но мне бы хотелось, чтобы я вскочил на подножку FRP немного раньше. Мне никогда не нравился RxSwift (или ReactiveCocoa во времена Obj-C), он никогда не работал до тех пор, пока я не начал использовать ReactiveKit и Bond еще в 2019 году. Теперь я использую Combine во всех своих приложениях с 2020 года, и не могу представить, что вернусь к старому способу создания приложений.

Если вы только начинаете разработку iOS, совет номер один – не перегружаться всеми вариантами архитектуры и не пытаться делать все идеально с самого начала. Просто используйте MVC, не волнуйтесь, получите ли вы в итоге массивные контроллеры представления или нет. Используйте раскадровку с переходами, если они вам нравятся, даже не думайте пока о координаторах. Забудьте о комбинированном и реактивном функциональном программировании.

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

– Какое будущее у iOS-разработки?

SwiftUI и async/await. Жаль, что будущее реальных приложений, где вы должны поддерживать старые версии iOS – это буквально несколько лет.

– Чем вы занимаетесь сейчас и какие у вас планы?

Я занимаюсь проектом для клиента, в котором также работаю над приложением iOS и серверным кодом. Планирую продолжать получать удовольствие как разработчик-фрилансер, принимая проекты, которые меня интересуют.

Мне почти 40 лет, и я не знаю, как долго захочу оставаться разработчиком или фрилансером. Так что со временем я, возможно, захочу больше сосредоточиться на том, чтобы быть наставником, чем мне действительно нравится заниматься сейчас несколько часов в неделю. Посмотрим 🙂
***

Если вы хотите получить необходимые знания в структурированном виде и без лишней воды, стоит обратить внимание на курс Факультета iOS-разработки образовательной онлайн-платформы GeekBrains. За 12 месяцев вы освоите два языка программирования и современные технологии создания приложений, научитесь писать чистый код и реализуете несколько проектов для портфолио. Обучение будут курировать эксперты-практики, а после защиты финального проекта студентам выдадут электронные сертификаты и дипломы о профессиональной переподготовке. HR-специалисты GeekBrains помогут выпускникам с трудоустройством.

29
Июн
2021

Как разрешить iphone обрабатывать js скрипты в браузере chrome, safary?

Столкнулся с блокировкой js скрипта в iphone 12 при загрузке веб-сайта. Простой скрипт, который переключает видимость меню и загружает пак иконок не работает. Кто с этим сталкивался, как обойти это ограничение без настроек в системе устрой…

21
Июн
2021

Apple Tech Business Week. День 3

Эксперты обсудят безопасность macOS, интерфейс-тренды, Combine, SwiftUI, проектирование сложных B2B-продуктов и организацию ИТ-офисов.
— Читать дальше «Apple Tech Business Week. День 3»

20
Июн
2021

Нужна помощь в копировании в буфер обмена в Safari(ios, mac и тд) JavaScript(React) Подкажите возможно кт то сталкивался с такой проблемой

function App() {

const copyToClipboard = (textToCopy: string) => {

var el: any = document.createElement(‘textarea’);
el.value = textToCopy;
el.setAttribute(‘readonly’, ”);
el.style = { position: ‘absolute’, lef…

11
Июн
2021

Пользователи Apple смогут использовать Face ID и Touch ID вместо пароля при регистрации на сайтах

Доступ к бета-версии новой функции появится до конца 2021 года.
— Читать дальше «Пользователи Apple смогут использовать Face ID и Touch ID вместо пароля при регистрации на сайтах»

09
Июн
2021

Хакатон INNOHACK 2.0

Команды из разработчиков, аналитиков, тестировщиков, DevOps-инженеров и UI/UX-дизайнеров должны решить одну из 5 бизнес-задач. Призовой фонд — 1,2 млн рублей. Заявки принимаются до 15 июня.
— Читать дальше «Хакатон INNOHACK 2.0»