Библиотека мобильного разработчика | Android, iOS, Swift, Retrofit, Moshi, Chuck
9.61K subscribers
1.65K photos
80 videos
52 files
4.46K links
Все самое полезное для мобильного разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4adec1b17b35b6c0d8389
Download Telegram
Организация представлений SwiftUI с помощью ToolbarContent и @ToolbarContentBuilder

По мере роста проектов SwiftUI одна из частых проблем — управление сложными иерархиями представлений. Даже простой экран может быстро превратиться в десятки вложенных модификаторов. После снятия лимита в 10 вложенных представлений стало проще писать глубоко вложенные body, но код стал труднее читать и сопровождать.

🔹 Разбираем крупные реализации body

Когда body растягивается на десятки строк, страдает читаемость. Лучше разбивать большие представления SwiftUI на мелкие подпредставления или выделять повторно используемые части в вычисляемые свойства или функции. Это сохраняет лаконичность и упрощает понимание, тестирование и повторное использование.

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

🔹 Проблема с панелями инструментов

Модификатор .toolbar позволяет создавать кнопки, меню и элементы управления, адаптируемые под разные платформы. Но если элементов становится много, код внутри .toolbar { ... } быстро теряет читаемость.
Перенести логику в вычисляемое свойство нельзя — .toolbar ожидает содержимое, соответствующее ToolbarContent.

🔹 ToolbarContent и @ToolbarContentBuilder

SwiftUI решает это с помощью:

ToolbarContent — протокола для элементов панели инструментов;
@ToolbarContentBuilder — билдера, создающего набор элементов панели.

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

Пример

struct DemoView: View {
@State private var message = "Hello, world!"

var body: some View {
NavigationStack {
Text(message)
.font(.title2)
.padding()
.navigationTitle("Home")
.toolbar { toolbarContent }
}
}

@ToolbarContentBuilder
private var toolbarContent: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {
Button { message = "Left tapped" } label: {
Label("Left", systemImage: "line.3.horizontal")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button { message = "Right tapped" } label: {
Label("Right", systemImage: "star")
}
}
}
}


🔹 Заключение

Использование @ToolbarContentBuilder для отделения содержимого панели инструментов от основного окна имеет ряд преимуществ:

Улучшена читаемость: Ваш body текст по-прежнему сосредоточен на вёрстке, а логика панели инструментов реализована в другом месте.
Лучшая организация: Группировка элементов панели инструментов в отдельном блоке позволяет с первого взгляда оценить их структуру и расположение.
Масштабируемость: Когда на панели инструментов появляется несколько кнопок, меню или условная логика, поддерживать её в рабочем состоянии становится намного проще.

🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🧠 Tachikoma — современный Swift SDK для работы с ИИ-провайдерами

Tachikoma — современный Swift AI SDK, который делает интеграцию с ИИ простой и естественной. Tachikoma предоставляет интеллектуальный, адаптивный интерфейс для ИИ-сервисов с полностью современным API на Swift.

Пример:

import Tachikoma  // Single unified module

// Simple text generation
let answer = try await generate("What is 2+2?", using: .openai(.gpt4o))
print(answer) // "4"

// With different models
let response1 = try await generate("Hello", using: .anthropic(.opus4))
let response2 = try await generate("Hello", using: .grok(.grok4))
let response3 = try await generate("Hello", using: .ollama(.llama33))


💻 Tachikoma на GitHub

🐸 Библиотека мобильного разработчика

#буст #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🔥 Вышла библиотека Any Language Model

Разработчик Мэтт Тиссен (Matt Thiessen) выпустил библиотеку Any Language Model для Swift. Она полностью совместима со стандартной Foundation Models, и с ней можно использовать любые LLM, а не только решения от Apple.

В 2025 году Apple представила библиотеку Foundation Models. С её помощью в приложения можно интегрировать модели от Apple: локальную LLM с 3 млрд параметров или облачную VLM. Разработчики в сообществе оценили удобный синтаксис адаптера и оптимизацию под устройства экосистемы, но привязка к определённым нейросетями разрушила потенциальную универсальность.

Проблему решил Мэтт Тиссен. В его API-совместимой библиотеке Any Language Model разработчики могут сами выбирать бэкенд для запуска моделей. Например, можно использовать Apple Foundation Models, CoreML, MLX, llama.cpp (GGUF), OpenAI API, Anthropic API или Google Gemini API. При этом не надо создавать отдельный адаптер для каждого LLM-провайдера.

Для установки Any Language Model в файле Package.swift надо указать следующую зависимость:

dependencies: [
.package(url: "https://github.com/mattt/AnyLanguageModel.git", from: "0.3.0")
]


Для снижения размера готового приложения можно импортировать только нужные провайдеры. Например, код импорта адаптеров для CoreML и MLX выглядит так:

dependencies: [
.package(
url: "https://github.com/mattt/AnyLanguageModel.git",
branch: "main",
traits: ["CoreML", "MLX"] // Enable CoreML and MLX support
)
]


Пример использования Any Language Model выглядит следующим образом:

import AnyLanguageModel

struct WeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"

@Generable
struct Arguments {
@Guide(description: "The city to fetch the weather for")
var city: String
}

func call(arguments: Arguments) async throws -> String {
"The weather in \(arguments.city) is sunny and 72°F / 23°C"
}
}

let model = SystemLanguageModel.default
let session = LanguageModelSession(model: model, tools: [WeatherTool()])

let response = try await session.respond {
Prompt("How's the weather in Cupertino?")
}
print(response.content)


➡️ Код библиотеки доступен на GitHub. Для работы нужен Swift 6.1+ и iOS 17.0, macOS 14.0 или visionOS 1.0.

🐸 Библиотека мобильного разработчика

#свежак #iOS #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Вложенные типы в Swift

Типы могут быть вложенными (nested). Класс или структура может содержать определение другого класса или структуры. Например:

class User{
var name: String
var age: Int
var profile: UserProfile

struct UserProfile{
var login: String
var password: String

func authenticate(_ login: String, _ password: String) -> Bool{
return self.login == login && self.password == password
}
}

init(name: String, age: Int, login: String, password: String){
self.name = name
self.age = age
self.profile = UserProfile(login: login, password: password)
}
}

var tom = User(name: "Tom", age: 23, login: "querty", password: "12345")
print(tom.profile.authenticate("sdf", "456")) // false
print(tom.profile.authenticate("querty", "12345")) // true


Здесь структура UserProfile является вложенной. Вложенные типы также могут определять свойства, методы, инициализаторы. А внешний тип может хранить объект вложенного типа.

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

var profile = User.UserProfile(login: "ssdf", password: "345")
var isLoged = profile.authenticate("ssdf", "345") // true


🐸 Библиотека мобильного разработчика

#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
5️⃣ вещей о Optional в Swift, которые всех сбивают с толку

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

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

Давайте рассмотрим сложные аспекты optional в Swift, которые всех сбивают с толку.

1️⃣ ? против !

Есть два способа сделать переменную optional с помощью ? и !, но каждый из них имеет разное значение.

?

Когда вы делаете любую переменную необязательной (optional) с помощью ?, это означает, что вам также придётся обрабатывать значение nil.

var name: String?

if let name = name {
print("Hello, \(name)")
} else {
print("No name found.")
}


Здесь мы использовали if-let для обработки имени. Если имя равно nil, выполняется блок else; в противном случае выполняется блок if.

!

Когда вы создаёте переменную с помощью !, это означает, что вам не нужно обрабатывать значение nil.

var userId: String!

print(userId.count)


Код выполняется успешно, но если значение равно нулю, приложение аварийно завершает работу.

Примечание. Используйте !, если вы уверены, что заданное значение не будет равно нулю, даже если оно объявлено как optional.

2️⃣ Цепочка optional переменных не останавливает выполнение

С помощью цепочки необязательных переменных (?) вы можете безопасно выполнить код с необязательной переменной.

let size = user?.name.count


Этот код работает нормально, даже если имя равно нулю. Всё выражение возвращает nil, но не приводит к крешу.

Необязательная цепочка (Optional Chaining) не остановит выполнение программы, если значение равно нулю. Взгляните на этот пример:

user?.logout() 
print("Done!")


Если user равен нулю, функция выхода из системы не будет вызвана, но приведенный ниже код будет выполнен.

3️⃣ if let — не всегда лучший выбор

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

if let name = data?.name {
if let city = data?.address?.city {
print("\(name) lives in \(city)")
}
}


Это затрудняет чтение вложенного кода.

Лучший способ:

guard let name = data?.name,
let city = data?.address?.city else {
return
}

print("\(name) lives in \(city)")


Еще лучший способ:

let city = data?.address?.city ?? "Unknown City"


4️⃣ Двойной optional (??)

Вы уже знакомы с таким кодом:

var name: String? = "Jayant"


Переменная name имеет строковое значение или может быть равна нулю.

Но видели ли вы что-то подобное?

let value: String?? = "Jayant"


Это называется Double Optional (??). Читается как Optional(optional("Jayant")).

Основной вариант использования — когда вы возвращаете необязательный параметр из функции и присваиваете эту функцию любой переменной, которая также возвращает optional параметр.

func getName() -> String? {
return nil
}

let name: String?? = getName()


Здесь функция getName() возвращает String?, а при назначении имени вы пишете String??, что означает optional, содержащую другую optional строку.

5️⃣ Optional коллекции — это не то же самое, что пустые

Взгляните на этот код:

var names: [String]? = []


На первый взгляд кажется, что это просто пустой массив, но на самом деле это optional массив, содержащий внутри себя пустой массив.

Необязательный массив может находиться в различных состояниях.

var names: [String]? = nil   // it's not an array
var names: [String]? = [] // Array exists, but it’s empty
var names: [String]? = ["Jayant", "Neha"] // Array with values


Давайте посмотрим, что произойдет, если вы это проверите:

var names: [String]? = []

if names != nil {
print("Array exists")
}

// output ---> Array exists


Как видите, массив существует и пуст.

🔹 Курс «Основы IT для непрограммистов»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🐸 Библиотека мобильного разработчика

#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM