Help us understand the problem. What is going on with this article?

swiftからC/ObjCを触る (例: String -> SHA256)

More than 3 years have passed since last update.

 Swiftを単体で弄るだけでは面白くありません。ご先祖様が残してくださったC/ObjC資産をどんどん使っていきましょう。
 ブリッジングヘッダーについて調べ始めたあたりの人を念頭に置いた記事です。参考になる記事の纏めも兼ねています。

C => ObjC => Swift

 Swiftは書いていて愉しい言語ですが、Swiftで書かれたソースはまだまだ多くありません。しかも名前がややこしいため、簡単な処理であっても調べるのが大変です。「swift やりたいこと」で検索したらCDアルバムやクルマ、謎の組織あるいは並列処理が得意な方のswiftが出てきてイラつく経験は誰しも通る道でしょう。
 そういう時には、諦めることなくC/ObjCのコードを検索してみましょう。C/ObjCで書けることはほとんどそのままSwiftで書けますし、C/ObjCで書かれたライブラリーはswiftから容易に利用できるからです。

CommonCrypto

 タイトルにもあるように、この記事では文字列からSHA256を得ることを例に挙げます。幸いなことに、この問題については「Swift SHA256」と検索することで参考になる記事をいくつも発見できます。
 StringからSHA256その他のハッシュ値を得るには、CommonCrypto の持つ関数を使えば簡単です。これはSwiftの一部ではなく、オープンソースのC言語製ライブラリーです。

 ここでいきなり「アルゴリズムが分かっているのだから、swiftらしさ全開で書き直すべきだ」という人は多分いないでしょう。自在にC/ObjC資産を使いまわすほうが、遥かに「Swiftらしい」に違いありません。

 ということで、まずはブリッジングヘッダーを書きましょう。そもそも作り方が分からんという方は参考記事をご覧ください。

Bridging-Header.h
#import <CommonCrypto/CommonDigest.h>

 これで準備が整います。簡単ですねー。

 ただし、Swiftで愉しく楽して簡単に使えるのはC/ObjCのヘッダーだけです。C++を使おうとすると結構な手間がかかります。

 ちなみに、自分で用意したライブラリー (homebrewでインストールしたライブラリーとか) を使う場合には、ライブラリーをリンクしてヘッダーをインクルードするだけでなく、Xcodeにおいて Header Search Paths を通さなければいけません。インクルードすべきファイルがどこにあるか、知らせる必要があるからです。
 個人的には /usr/local/include をよく書き足します。

Objective-C without the baggage of C

f32262.png

 それってsmalltalk

 フェデリギお兄さん曰く、ObjCからCを取り外すとSwiftなんだそうです。ということで、Swiftにはポインタとかchar配列とかいう古代C言語は出てこない… ように見えます。
 しかし、それはピュアなSwiftの世界に出てこないだけの話であって、「出せない」わけではないのです。

extension String {
    var md5: String {
        let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
        let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)

        CC_MD5(str!, strLen, result)

        var hash = NSMutableString()
        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }

        result.dealloc(digestLen)

        return String(format: hash as String)
    }
}
// 注:一部修正有

 君なんか写真と違わない?
 見るからにC言語っぽいですね。

 Swiftには、正常に動くC言語のロジックをほとんどそのまま持ち込むために必要な道具が揃っています。使えるものを再利用するわけですから、極めて生産的です。「なんかCっぽい」書き方もSwiftらしさと考えるべきでしょう。
 ただ、上の例はちょっと幾ら何でもC言語の風味が強すぎるような気がします。str! とアンラップしていることが気になりますし、メソッドなので使うときに () を付ける必要があるのも面倒です。

 ということで、書き直しましょう。いかにもSwiftっぽく。

extension String {
    var sha256: String! {
        if let cstr = self.cStringUsingEncoding(NSUTF8StringEncoding) {
            var chars = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
            CC_SHA256(
                cstr,
                CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)),
                &chars
            )
            return chars.map { String(format: "%02X", $0) }.reduce("", combine: +)
        }
        return nil
    }
}

// 使い方
println("Swift".sha256)
// -> AE8ED2743925477EAFAD0DFDB0D0832702634DA4DC6EC1AEFA4F3A61953E93F0

 オプショナル型のアンラップはif letで回避し、ポインターはArrayで代替し、forは使わずmapで済ませることができます。
 記憶力の貧弱な現代人としては、ポインターを直接触ったり、メモリ管理に手を出すのはなるべく避けたいところです。

 なお、ここでの戻り値はString!型にしています。これは真っ当な理由があるわけではなく、cStringUsingEncoding(NSUTF8StringEncoding)がどういう場合にnilを返すのか分からないので「たぶんnilが返ってくることはないだろう」と決めつけただけです。
 「絶対にnilが返ることはない」と断定できるなら、if letを使わずアンラップする方が単純なのですが… どうなんでしょうか。

 以上です。Swift/ObjC/Cの連携は記事も少なめですが、触れば案外なんとかなるので頑張ってください。

おまけ

 最初はif letのかわりにmapを使って書いたんですが、やたら見た目が分かりにくいので止めました。

extension String {
    var sha256: String! {
        return self.cStringUsingEncoding(NSUTF8StringEncoding).map { cstr in
            var chars = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
            CC_SHA256(
                cstr,
                CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)),
                &chars
            )
            return chars.map { String(format: "%02X", $0) }.reduce("", combine: +)
        }
    }
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした