そーす

福岡在住のプログラマ

Obj-cとSwift連携の注意

f:id:saburesan:20160714124845p:plain

結論 obj-cからswiftのプログラムを呼ぶ際はSwift独自の機能に気をつけよう(適当

ハマりました。

UICollectionViewDelegateFlowLayoutのメソッドをよく使うものはProtocolExtensionで共通化しようと思いました。

protocol BaseCollectionViewDelegate: UICollectionViewDelegateFlowLayout {
    var lineSpace: CGFloat { get }
    var interitemSpace: CGFloat { get }
    var numOfRow: CGFloat { get }
}

extension BaseCollectionViewDelegate where Self: UIViewController {
    var lineSpace: CGFloat { return 1 }
    var interitemSpace: CGFloat { return 1 }
    var numOfRow: CGFloat { return 2 }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        let itemGridSize = (self.view.frame.width - (numOfRow - 1) * interitemSpace) / numOfRow
        return CGSizeMake(itemGridSize, Dimension.homeLatestItemHeight)
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
        return lineSpace
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
        return interitemSpace
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        return CGSizeMake(self.view.frame.width, Dimension.indicatorFooterHeight)
    }
}


class MyViewController: UIViewController, BaseCollectionViewDelegate {

}

こんな感じで。

BaseCollectionViewDelegateはプロパティ3つを新たに追加しているので実装が必要ですが、extensionでcomputed valueとして宣言したので、ViewController側では実装が不要ですね。

この状態でコンパイルが通るのでてっきりExtensionは上手くいってるもんだと思ってました。

しかし実行すると、レイアウトはExtensionで実装したようにはなりませんでした。

原因

SwiftからObjcのプログラムを呼ぶ際にSwiftの機能が使えなくなるから。

UICollectionViewDelegateFlowLayoutはObjective-cのProtocolなのでそれに対してSwiftの機能のProtocolExtensionは提供できないぜ、ってことですね。

しかしながら、

import Foundation

class Hoge {}

@objc protocol Hogelable{
  optional func hoge()
}

protocol Hogelable2: Hogelable {}

extension Hogelable2 where Self: Hoge {
    func hoge(){
      print("Hogelable where Self: Hoge")
    }
}

class Fuga: Hoge, Hogelable2{}

let fuga = Fuga()
fuga.hoge()

これは上手く行くので、「@ObjcをつけるだけでSwiftの機能が制限される」わけではなく、 「Objc<->Swiftで書いたプログラムを呼び出す際にSwiftの機能が制限される」、ということですかね。

このあたりあまり知らなかった。

コンパルエラーにもならないので気をつけないといけないですね。