Категория для класса obj-c, полученного из swift - PullRequest
0 голосов
/ 06 июля 2018

Я запускаю проект с большим количеством устаревшего кода с использованием objc и swift. Я использовал objc MPOldKeychainManager, который сейчас устарел, и нужно использовать swift's NewKeychainManager.

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

  • присвоение имени классу, производному от NewKeychainManager (отображается в "myTarget-Swift.h"), до "MPOldKeychainManager"
  • удаление декларации objc MPOldKeychainManager

... надеясь, что категории все еще будут работать.

objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}

К сожалению, старые расширения не могут видеть MPOldKeychainManager (полученный из swift), хотя я обновил импортированный заголовок до myTarget-Swift.h

#import "myTarget-Swift.h" //previously - objc "MPOldKeychainManager.h"
@interface MPOldKeychainManager (Authentication)

Вопрос: возможно ли использовать категории для классов objc, полученных из swift?

  • Я уже попробовал совершенно новое имя
  • Я уже пробовал множество чистых сборок

1 Ответ

0 голосов
/ 11 июля 2018

Если вы еще этого не видели, вот полезный ресурс для перехода с Objective-C на Swift: https://developer.apple.com/documentation/swift/migrating_your_objective_c_code_to_swift. Среди прочего, в нем говорится, что нельзя подкласс класса Swift в Objective-C. То, что вы пытаетесь сделать, это указать другое имя Objective-C MPOldKeychainManager для класса NewKeychainManager Swift.

Это на самом деле будет работать, если вы добавите амперсанд до objc, например:

@objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}

Затем вы можете использовать все ваши существующие категории в Objective-C. Однако у вас возникнут проблемы с их использованием в Swift, поскольку для использования в Swift они должны быть доступны в заголовке моста, и вы не сможете использовать имя класса Objective-C (MPOldKeychainManager) в заголовке моста.

Однако вы можете написать класс-оболочку Objective-C, который будет иметь метод, соответствующий каждому методу категории, а также указатель NewKeychainManager. Затем метод-оболочка может делегироваться методу категории, который доступен для кода Objective-C, поэтому вам не придется повторно реализовывать методы категории в Swift.

Допустим, у категории Objective-C есть метод authenticateUser::

@interface MPOldKeychainManager (Authentication)

-(void)authenticateUser:(int32_t)uid;

@end

Метод можно обернуть следующим образом:

@interface OldKCMWrapper : NSObject

+(void)authenticateUser:(int32_t)uid withManager:(NewKeychainManager*)inst;

@end

Это объявление интерфейса должно быть доступно, прямо или косвенно, через заголовок моста. Затем где-нибудь в вашем коде Objective-C оболочка может быть реализована следующим образом:

@implementation OldKCMWrapper

+(void)authenticateUser:(int32_t)uid withManager:(MPOldKeychainManager*)inst {
    [inst authenticateUser:uid];
}

@end

Затем обертку можно использовать в коде Swift, например ::

let kcm = NewKeychainManager()
OldKCMWrapper.authenticateUser(321, with: kcm)

На самом деле, оболочку можно использовать в расширении Swift NewKeychainManager. У вас по-прежнему будет расширение Swift с эквивалентами всех методов категории Objective-C, но вам не придется повторно реализовывать их код в Swift: методы в расширении просто делегируют оболочке.

Надеюсь, это полезно. Есть и другие способы реализации этой идеи, возможно, более элегантные.

...