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))
#буст #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Разработчик Мэтт Тиссен (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)
#свежак #iOS #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Типы могут быть вложенными (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
Please open Telegram to view this post
VIEW IN TELEGRAM
Optional (необязательный) означает, что переменная или данные могут быть равны нулю, и вы обрабатываете эти нулевые значения с помощью некоторых ключевых слов Swift.
Если вы обнаруживали
nil и развертывали его с помощью optional для обработки, это значит, что вы знакомы с таким типом переменных.Давайте рассмотрим сложные аспекты
optional в Swift, которые всех сбивают с толку.? против !Есть два способа сделать переменную 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.С помощью цепочки необязательных переменных (?) вы можете безопасно выполнить код с необязательной переменной.
let size = user?.name.count
Этот код работает нормально, даже если имя равно нулю. Всё выражение возвращает
nil, но не приводит к крешу.Необязательная цепочка (Optional Chaining) не остановит выполнение программы, если значение равно нулю. Взгляните на этот пример:
user?.logout()
print("Done!")
Если
user равен нулю, функция выхода из системы не будет вызвана, но приведенный ниже код будет выполнен.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"
??)Вы уже знакомы с таким кодом:
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 строку.Взгляните на этот код:
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
Please open Telegram to view this post
VIEW IN TELEGRAM
При создании интерфейсов на основе данных в SwiftUI вам часто будет требоваться визуализировать числовое значение или управлять им. Для этой цели можно использовать три встроенных представления —
Gauge, ProgressView, и Slider.Хотя на первый взгляд они могут показаться похожими, они существенно различаются по назначению и способу взаимодействия.
🔹 Индикатор — отображение значения в диапазоне
Представленный в iOS 16 вид
Gauge предназначен для отображения значения, а не для того, чтобы пользователи могли его изменять.Он идеально подходит для случаев, когда вам нужен индикатор только для чтения, например для отображения уровня заряда батареи, температуры или загрузки процессора.
Gauge(value: currentTemp, in: 0...100) {
Text("Temperature")
} currentValueLabel: {
Text("\(Int(currentTemp))°C")
}
.tint(.orange)
Gauge(value: currentTemp, in: 0...100) {
Text("Temperature")
} currentValueLabel: {
Text("\(Int(currentTemp))°C")
}
.tint(.orange)Индикатор может быть выполнен в нескольких стилях (
.linearCapacity, .accessoryCircular и других) и естественным образом адаптируется к различным макетам — от круглых индикаторов в стиле приборной панели до компактных виджетов.Используйте
Gauge , когда:🔹 ProgressView — индикатор выполнения задачи
ProgressView предназначен для отслеживания прогресса, а не для отображения числовых значений. Он показывает, какая часть задачи выполнена, либо детерминированно (известная доля), либо неопределённо (индикатор вращения).ProgressView(value: progress, total: 1.0)
.tint(.green)
ProgressView(value: progress, total: 1.0)
.tint(.green)
Он хорошо подходит для экранов загрузки/выгрузки, процессов адаптации или длительных операций.
Ключевое отличие от
Gauge заключается в контексте: значение представляет не реальное измерение, а состояние процесса.Используйте
ProgressView , когда:🔹 Ползунок — обеспечивает прямое управление пользователем
В отличие от
Gauge и ProgressView, Slider позволяет вводить данные. Это правильный выбор, если вы хотите, чтобы пользователь мог установить или изменить числовое значение, например яркость, громкость или интенсивность фильтра.Slider(value: $volume, in: 0...100) {
Text("Volume")
}
.tint(.blue)
Slider(value: $volume, in: 0...100) {
Text("Volume")
}
.tint(.blue)Slider напрямую связывается со свойством состояния с помощью Binding, что делает его оптимальным вариантом для любой интерактивной числовой настройки.Используйте
Slider , когда:🔹 Выбор правильного представления
При выборе из трёх вариантов:
Gauge.ProgressView.Slider.Каждый из этих видов соответствует принципам дизайна Apple, нап равленным на ясность и доступность. Понимание их сути поможет вам создавать интерфейсы, которые будут выглядеть правильно и вести себя естественно в экосистеме SwiftUI.
🔸 Курс «Основы IT для непрограммистов»
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#PixelPerfect #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Миграция базы данных требуется всякий раз, когда нам нужно внести изменения в модель Core Data. Для определённого набора изменений Core Data может выполнить почти автоматическую миграцию, которая называется облегчённой миграцией.
Это позволяет нам развивать нашу схему без потери существующих данных или необходимости вручную определять правила сопоставления.
Примеры поддерживаемых изменений: добавление, переименование или удаление сущностей, атрибутов или связей, а также изменение типов связей.
Когда изменения в нашей модели выходят за рамки того, что может определить Core Data, нам необходимо выполнить сложную (ручную) миграцию.
Давайте рассмотрим, как выполнить упрощённую миграцию в Core Data.
🔹 Как выполнить упрощённую миграцию в Core Data
Чтобы выполнить упрощённую миграцию в Core Data, необходимо выполнить следующие действия:
1. Включение облегченной миграции
2. Создание новой версии модели
3. Изменение новой модели
Давайте рассмотрим эти этапы более подробно.
Когда мы используем класс NSPersistentContainer для создания стека Core Data и управления им, нам не нужно выполнять никаких дополнительных действий по настройке, облегчённая миграция активируется автоматически.
Если мы создадим собственный стек Core Data вручную, то сможем явно включить эти параметры при добавлении хранилища:
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
let options = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true
]
do {
try coordinator.addPersistentStore(
ofType: NSSQLiteStoreType,
configurationName: nil,
at: url,
options: options
)
} catch {
// handle migration failure
}
Эти два параметра позволяют Core Data автоматически определять модель сопоставления и переносить данные при обнаружении совместимого изменения схемы.
Для автоматической миграции Core Data нужны исходная и конечная версии нашей модели данных.
Если мы изменим существующий
.xcdatamodeld сразу после выпуска нашего приложения, Core Data не будет знать, как выполнить миграцию, и выдаст ошибку о несовместимости моделей.Чтобы избежать этого, мы создаем новую версию модели, выбрав
Редактор > Добавить версию модели в меню Xcode. Это добавит новый файл .xcdatamodel в наш проект.После добавления новой версии мы можем установить текущую версию в инспекторе файлов Xcode. Теперь мы можем спокойно вносить изменения, не нарушая работу существующих хранилищ.
Теперь мы можем вносить изменения в нашу модель. Чтобы Core Data могла создать предполагаемую модель сопоставления, изменения должны соответствовать определённому шаблону, например:
let inferred = try? NSMappingModel.inferredMappingModel(
forSourceModel: oldModel,
destinationModel: newModel
)
if inferred != nil {
// Lightweight migration is possible
}
Если этот вызов вернёт nil, Core Data не сможет определить сопоставление, а это значит, что нам потребуется более сложная миграция вручную.
🔸 Курс «Архитектуры и шаблоны проектирования»
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#АрхитектурныйКод #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
xcrun instruments – профилирование и анализ производительности iOS-приложенийПрофессиональный инструмент для измерения производительности, поиска утечек памяти и анализа энергопотребления прямо из командной строки.
Запуск профилирования:
# Запуск Instruments с шаблоном Allocations
xcrun instruments -t "Allocations" -l 30000 MyApp.app
# Профилирование времени запуска
xcrun instruments -t "Time Profiler" MyApp.app -e UIResponder 30
Автоматизация тестов:
# Запуск с записью результатов в .trace файл
xcrun instruments -t "Time Profiler" -D performance.trace MyApp.app
1. Анализ утечек памяти:
# Запуск Leaks теста на 60 секунд
xcrun instruments -t "Leaks" MyApp.app -l 60000
# С сохранением результатов
xcrun instruments -t "Leaks" -D memory_leaks.trace MyApp.app -l 30000
2. Измерение времени запуска:
# Профилирование запуска приложения
xcrun instruments -t "Time Profiler" MyApp.app -e UIResponder 10 -D launch.trace
3. Анализ энергопотребления:
# Мониторинг использования батареи
xcrun instruments -t "Energy Log" MyApp.app -l 120000
Allocations – отслеживание памяти и объектовTime Profiler – анализ производительности кодаLeaks – поиск утечек памятиEnergy Log – мониторинг энергопотребленияNetwork – анализ сетевой активностиCore Data – профилирование операций с базой данныхПрофилирование конкретного процесса:
# Присоединение к запущенному процессу
xcrun instruments -t "Time Profiler" -p 1234
Создание кастомных конфигураций:
# Сохранение конфигурации профилирования
xcrun instruments -t "Time Profiler" -c "My Custom Config" MyApp.app
Пакетное тестирование:
#!/bin/bash
# performance_test.sh
APP="MyApp.app"
TRACE_DIR="./traces"
mkdir -p $TRACE_DIR
echo "Running performance tests..."
xcrun instruments -t "Time Profiler" -D "$TRACE_DIR/time_profiler.trace" $APP -l 30000
xcrun instruments -t "Allocations" -D "$TRACE_DIR/allocations.trace" $APP -l 30000
Какие инструменты профилирования вы используете в своих проектах?
🔸 Курс «Основы IT для непрограммистов»
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#буст #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1🤝1
В Swift нам доступен механизм перегрузки функций, то есть мы можем определять функции с одним и тем же именем, но разным количеством или типом параметров:
func sum(_ x: Int, _ y : Int){
print(x+y)
}
func sum(_ x: Double, _ y: Double){
print(x+y)
}
func sum(_ x: Int, _ y: Int, _ z: Int ){
print(x + y + z)
}
sum(1, 2) // 3
sum(1.2, 2.3) // 3.5
sum(2, 3, 4) // 9В данном случае все три функции называются sum, но так как они отличаются либо по количеству параметров, либо по их типу. При вызове данной функции Swift по типу и количеству параметров сможет распознать, какую именно версию функции sum надо использовать.
Также перегруженные версии одной функции могут отличаться по типу возвращаемого значения:
func sum(_ x: Int, _ y : Int) -> Int{
return x + y
}
func sum(_ x: Int, _ y : Int) -> Double{
return 2 * Double(x + y) // преобразует результат в Double
}
let a : Int = sum(1, 2) // 3
let b : Double = sum(1, 2) // 6.0
print(a) // 3
print(b) // 6.0В данном случае определены две версии функции sum, которые совпадают по типу и количеству параметров, но различаются по типу возвращаемого значения.
Константа a представляет тип
Int, поэтому в выражении let a : Int = sum(1, 2) компилятор увидит, что необходима версия, которая возвращает значение Int, и будет использовать эту версию. Аналогично в выражении let b : Double = sum(1, 2) константа b представляет тип Double, поэтому здесь будет применяться версия функции sum, которая возвращает значение Double. То есть в данном случае у нас ошибок не возникнет.Однако теперь рассмотрим другую ситуацию:
func sum(_ x: Int, _ y : Int) -> Int{
return x + y
}
func sum(_ x: Int, _ y : Int) -> Double{
return 2 * Double(x + y) // преобразует результат в Double
}
let a = sum(1, 2) // Ошибка
let b = sum(1, 2) // ОшибкаЗдесь явным образом не указано, какой тип представляют константы a и b, поэтому их тип будет выводиться изрезультата вызова
sum(1, 2). Но теперь компилятор не знает, какую именно версию функции sum использовать, так как тип констант неизвестен. В итоге в данном случае мы столкнемся с ошибкой.🔹 Курс «Основы IT для непрограммистов»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib
#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Современные архитектуры iOS часто основаны на модульности, а Swift Package Manager стал предпочтительным способом структурирования повторно используемых компонентов. Каждый модуль может содержать собственные ресурсы, в том числе локализованные строки, изображения и другие материалы. На бумаге это позволяет разрабатывать и выпускать каждую функцию независимо от других. Однако на практике локализация в Swift Packages не всегда работает так, как ожидают разработчики, после интеграции этих модулей в приложение.
Часто возникает одна и та же проблема: локализованные строки в пакете Swift работают во время разработки, но как только пакет используется в реальном приложении, все строки возвращаются к английскому варианту. Даже если пакет настроен правильно, локализованные
.lproj папки игнорируются во время выполнения. Такое поведение может сбивать с толку, если вы ожидаете, что локализация будет работать автоматически во всех пакетах.🔹 Почему игнорируются локализации в пакетах Swift
Пакеты Swift создаются в виде собственных наборов ресурсов, отдельных от основного набора ресурсов приложения. Несмотря на то, что
.strings файлы хранятся в этих наборах и доступ к ним осуществляется через Bundle.module, система по-прежнему полагается на то, что хост-приложение объявляет, какие локализации оно поддерживает.Если в конфигурации основного приложения явно не указан язык, iOS считает этот язык неподдерживаемым на уровне приложения. В результате среда выполнения возвращается к языку разработки по умолчанию, обычно английскому, независимо от того, какой язык доступен в пакете.
Это ожидаемое поведение системы локализации Apple, а не ошибка. Только основной пакет определяет языковые возможности приложения.
🔹 Необходимое исправление: объявление локализаций в основном приложении
Чтобы локализация Swift Package работала стабильно, в основном приложении необходимо объявить все поддерживаемые языки в
Info.plist под ключом CFBundleLocalizations. После добавления этого списка система будет корректно обрабатывать локализованные строки из всех пакетов ресурсов, в том числе созданных с помощью Swift Package.Минимальная конфигурация может выглядеть следующим образом:
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>fr</string>
</array>
После добавления этой записи ранее игнорируемые
.lproj папки внутри пакетов Swift Package начинают работать сразу.🔹 Заключение
Локализация пакетов Swift Package не представляет сложности, если вы понимаете, как система определяет языки в пакетах. Даже если модули содержат собственные
.lproj папки, основное приложение должно явно указывать все поддерживаемые локализации. Добавление CFBundleLocalizations в Info.plist решает проблему возврата к английскому языку и обеспечивает правильное поведение локализованных ресурсов из пакетов Swift Package.🔸 Курс «Архитектуры и шаблоны проектирования»
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#АрхитектурныйКод #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
В SwiftUI одним из наиболее элегантных способов настройки визуальных элементов является внедрение стилей. Вместо того чтобы вручную передавать явные цвета или параметры стиля по дереву представлений, SwiftUI позволяет внедрять информацию о стиле на более высоком уровне с помощью таких модификаторов, как
.foregroundStyle(), .backgroundStyle(), и .tint(). Эти внедренные стили могут использоваться любым дочерним представлением, которое ссылается на соответствующие динамические значения.Этот подход не только лаконичен, но и позволяет комбинировать элементы и использовать декларативный подход, что полностью соответствует философии дизайна SwiftUI.
Вот простой пример, демонстрирующий, как стили могут неявно передаваться по иерархии представлений:
struct DemoView: View {
var body: some View {
DetailView()
.foregroundStyle(.blue)
.backgroundStyle(.pink)
.tint(.yellow)
}
}В этом примере
DemoView применяет три модификатора стиля:.foregroundStyle(.blue) определяет стиль переднего плана, который будет наследоваться дочерними элементами, ссылающимися на .foreground.backgroundStyle(.pink) добавляет стиль фона..tint(.yellow) устанавливает глобальный оттенок, который влияет на элементы, использующие .tint.Ни один из этих модификаторов не ссылается явно на внутреннюю реализацию DetailView, что делает код модульным и слабосвязанным.
Теперь давайте посмотрим, как
DetailView использует эти стили:struct DetailView: View {
var body: some View {
Text("Primary text")
.foregroundStyle(.background)
.background(.foreground)
Text("Secondary text")
.foregroundStyle(.tint)
}
}Здесь:
.foregroundStyle(.background) указывает тексту использовать заданный стиль фона (в данном случае .pink)..background(.foreground) устанавливает для фона текста стиль переднего плана (в данном случае .blue)..foregroundStyle(.tint), которое соответствует .yellow в родительском представлении.Такой подход позволяет создавать многократно используемые представления с учётом темы. Представлениям не нужно знать, какие конкретные цвета они будут отображать — им нужно лишь обращаться к динамическим стилям, определяемым средой, таким как
.foreground, .background, или .tint.Используя стилистические роли и подставляя значения извне, мы получаем возможность точно контролировать внешний вид, не перегружая внутренние компоненты представления явными параметрами.
🔸 Курс «Основы IT для непрограммистов»
🔸 Получить консультацию менеджера
🔸 Сайт Академии 🔸 Сайт Proglib
#PixelPerfect #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM