Как вызвать Objective-C код из Swift: дружба языков в iOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Objective-C и Swift: два мира, один проект

Вы пишете на Swift, но в вашем распоряжении — библиотека, написанная на Objective-C, или старый, но проверенный код на ObjC. Знакомая ситуация? Не переживайте: Apple предусмотрела отличный механизм для совместной работы этих двух языков. В этой статье мы разберём, как вызвать Objective-C код из Swift — быстро, просто и без головной боли.

Этот вопрос — один из самых популярных на Stack Overflow (тысячи просмотров и сотни голосов). И не зря: умение интегрировать Objective-C в Swift открывает доступ к огромному наследию библиотек и кода, написанных за десятилетия развития iOS. Давайте наконец разберёмся, как это работает.

Что такое Bridging Header и зачем он нужен?

Секрет дружбы Swift и Objective-C — специальный файл, который называется Bridging Header (мостовой заголовок). Это что-то вроде переводчика: он сообщает Swift, какие классы и функции на Objective-C доступны, и как их правильно вызывать.

Когда вы создаёте файл моста, компилятор автоматически подхватывает его и делает все объявления из Objective-C видимыми в Swift. Вы просто импортируете заголовки ObjC в этот файл — и готово.

Как создать Bridging Header в Xcode

Самый простой способ — дать Xcode сделать это за вас. Просто добавьте в проект новый файл Objective-C (File → New → File → Objective-C File). Xcode спросит: «Хотите создать Bridging Header?» — отвечайте «Да». После этого в проекте появится файл с именем YourProjectName-Bridging-Header.h.

Если вы уже работаете с проектом и хотите добавить мост вручную, сделайте так:

  • Создайте новый заголовочный файл (File → New → File → Header File) и назовите его, например, MyProject-Bridging-Header.h.
  • В настройках проекта (Build Settings) найдите параметр Objective-C Bridging Header и укажите путь к вашему файлу.

Готово. Теперь всё, что вы пропишете в этом файле, будет доступно в Swift.

Пишем Objective-C класс и подключаем его к Swift

Давайте создадим простой класс на Objective-C, который будет приветствовать пользователя. Сначала — заголовочный файл (Greeter.h):

// Greeter.h
#import <Foundation/Foundation.h>

@interface Greeter : NSObject

- (NSString *)greetWithName:(NSString *)name;

@end

А теперь — реализация (Greeter.m):

// Greeter.m
#import "Greeter.h"

@implementation Greeter

- (NSString *)greetWithName:(NSString *)name {
    return [NSString stringWithFormat:@"Привет, %@! Рад познакомиться.", name];
}

@end

Теперь — самое важное. Откройте ваш Bridging Header (например, MyProject-Bridging-Header.h) и добавьте туда:

// MyProject-Bridging-Header.h
#import "Greeter.h"

Всё! Теперь вы можете использовать класс Greeter в Swift так, как будто он написан на Swift:

// ViewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let greeter = Greeter()
        let message = greeter.greet(withName: "Алексей")
        print(message) // Выведет: "Привет, Алексей! Рад познакомиться."
    }
}

Обратите внимание: метод greetWithName: в Swift трансформируется в greet(withName:). Это автоматическая адаптация имен методов из Objective-C в Swift-стиль.

Работа с Objective-C фреймворками и библиотеками

Часто вам нужно подключить не свой код, а готовую библиотеку на Objective-C (например, старую версию AFNetworking или CocoaLumberjack). Тут всё ещё проще:

  • Установите библиотеку через CocoaPods, Carthage или SPM.
  • Импортируйте её заголовки в ваш Bridging Header.
  • Используйте в Swift как обычно.

Пример для библиотеки SomeOldLibrary:

// В Bridging Header
#import <SomeOldLibrary/SomeOldLibrary.h>

Теперь все классы и функции этой библиотеки доступны в вашем Swift-коде. Удобно, правда?

Типичные проблемы и их решения

Даже при правильной настройке могут возникнуть сложности. Вот самые частые:

  • «Use of unresolved identifier» — скорее всего, вы забыли импортировать заголовок в Bridging Header или не указали путь к нему в настройках.
  • «Include of non-modular header inside framework module» — возникает, если вы пытаетесь импортировать заголовки из фреймворка, который не настроен как модуль. Решение: добавьте #import в файл .m, а не в .h, или используйте @import.
  • Метод не виден в Swift — проверьте, не помечен ли он как __attribute__((unavailable)) или не использует ли типы, которые не поддерживаются в Swift (например, NSInteger * без звёздочки).
  • Проблемы с именами — Objective-C методы с несколькими параметрами в Swift могут получить другое имя. Используйте NS_SWIFT_NAME для переименования.

Пример ручного переименования метода

// Objective-C
- (NSString *)fullNameForFirstName:(NSString *)first lastName:(NSString *)last
    NS_SWIFT_NAME(fullName(first:last:));

// В Swift
let name = person.fullName(first: "Иван", last: "Петров")

Когда стоит использовать Objective-C в Swift-проекте?

Интеграция Objective-C в Swift — это мощный инструмент, но не злоупотребляйте им. Вот сценарии, где это оправдано:

  • Легаси-код — вы переносите старый проект на Swift постепенно.
  • Библиотеки без Swift-аналогов — некоторые специфические SDK (например, для работы с железом) существуют только на ObjC.
  • Производительность — в редких случаях ObjC может быть быстрее для определённых низкоуровневых операций.

Однако для нового кода лучше использовать Swift: он безопаснее, читаемее и активно развивается Apple.

Альтернативы: можно ли обойтись без моста?

Если вы не хотите создавать Bridging Header, есть другой путь — использовать @objc и dynamic в Swift-коде для обратной совместимости. Но это работает только для вызова Swift из Objective-C, а не наоборот.

Для вызова ObjC из Swift мост — единственный прямой способ. Однако вы можете обернуть Objective-C класс в Swift-обёртку и работать только с ней:

// Swift обёртка
class SwiftGreeter {
    private let objcGreeter = Greeter()
    
    func greet(name: String) -> String {                

Блоги

Рекомендаций по книгам