Как получить доступ к частным API-интерфейсам iOS в Swift?

Как я могу назвать непубличные функции iOS и получить доступ к непубличным функциям от Swift? В частности, я хотел бы использовать один непубличный класс в каркасе QuartzCore.

Одним из решений, которые пришли мне на ум, является создание проекта "моста" Objective-C, который бы обернул эти непубличные API-интерфейсы в общедоступные, а затем вызвал эти функции Objective-C из Swift. Тем не менее, теперь мое решение является чистым Swift, и я бы предпочел сохранить его таким образом. Есть ли еще более простой путь? (например, добавив что-то в Objective-C мостовой файл заголовка)

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

Ответ 1

Вы можете сделать тот самый трюк, который вы сделали бы, если бы ваш проект был Objective-C.

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

В демонстрационных целях я буду выступать во внутренности NSBigMutableString, частного подкласса NSMutableString, и я назову его метод _isMarkedAsImmutable.

Обратите внимание, что здесь весь класс сам по себе является частным, поэтому я должен сначала объявить его как подкласс своего реального суперкласса. Из-за этого я мог бы также объявить метод прямо в объявлении самого класса; что, однако, не продемонстрировало бы использование категории (Private) трюков.

Если ваш класс является общедоступным, то, очевидно, вам понадобится только категория, и вам не понадобится (повторно) объявить сам класс.

Bridge.h:

#import <Foundation/Foundation.h>

@interface NSMutableString (Private)
- (BOOL)_isMarkedAsImmutable; // this is a lie
@end

@interface NSBigMutableString : NSMutableString
@end

s.swift:

let s = NSBigMutableString()
println(s._isMarkedAsImmutable())

Скомпилировать и запустить:

$ xcrun -sdk macosx swiftc -o foo -import-objc-header Bridge.h s.swift
$ ./foo
false
$

Ответ 2

Вы можете получить частные классы, используя NSClassFromString: и взаимодействовать с ними, используя performSelector.