Posted at

SwiftのNSDataDetectorクラスで文字列内にあるURLを検出する

More than 1 year has passed since last update.


概要

NSDataDetector クラスを利用して URL を検出し、その結果を考察する。


今回の動作確認環境

Xcode 8.3.3 + Swift 3.1 + Playground


ソースコード

import UIKit

/**
文字列内のURLを検出する
- parameter str: 調査対象の文字列
- returns: 検出したURL情報(NSTextCheckingResult)の配列
*/

func detectLinks(_ str: String) -> [NSTextCheckingResult] {
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
if let d = detector {
return d.matches(in: str, range: NSMakeRange(0, str.characters.count))
} else {
return []
}
}

/**
URL情報を出力する
- parameter links: URL情報(NSTextCheckingResult)の配列
*/

func printLinks(_ links: [NSTextCheckingResult]) {
links.forEach {
// NSRange
print("range.location: \($0.range.location)")
print("range.length: \($0.range.length)")
// URL?
print("url: \(String(describing: $0.url))")
print("")
}
}

// テストパターン
let tests: [String] = [
"hello, world", // URL無し
"https://niwasawa.github.io/ hello", // 文字列の先頭にURL
"hello https://niwasawa.github.io/", // 文字列の末尾にURL
"hello https://niwasawa.github.io/ http://d.hatena.ne.jp/niwasawa/ goodbye", // 複数のURL
"hello https://niwasawa.github.io/ https://niwasawa.github.io/ goodbye", // 同じURLが複数出現
"hellohttps://niwasawa.github.io/ http://d.hatena.ne.jp/niwasawa/ goodbye", // URLの前に文字列
"https://niwasawa.github.io/あ http://d.hatena.ne.jp/niwasawa/%E3%81%82", // 日本語を含むURL
"http://a", // 不完全なURL
"hoge://a", // hogeなURL
]

tests.forEach {
let links = detectLinks($0)
print("======================================================================")
print("test: [\($0)]\n")
printLinks(links)
}


出力結果

======================================================================

test: [hello, world]

======================================================================
test: [https://niwasawa.github.io/ hello]

range.location: 0
range.length: 27
url: Optional(https://niwasawa.github.io/)

======================================================================
test: [hello https://niwasawa.github.io/]

range.location: 6
range.length: 27
url: Optional(https://niwasawa.github.io/)

======================================================================
test: [hello https://niwasawa.github.io/ http://d.hatena.ne.jp/niwasawa/ goodbye]

range.location: 6
range.length: 27
url: Optional(https://niwasawa.github.io/)

range.location: 34
range.length: 31
url: Optional(http://d.hatena.ne.jp/niwasawa/)

======================================================================
test: [hello https://niwasawa.github.io/ https://niwasawa.github.io/ goodbye]

range.location: 6
range.length: 27
url: Optional(https://niwasawa.github.io/)

range.location: 34
range.length: 27
url: Optional(https://niwasawa.github.io/)

======================================================================
test: [hellohttps://niwasawa.github.io/ http://d.hatena.ne.jp/niwasawa/ goodbye]

range.location: 0
range.length: 32
url: Optional(hellohttps://niwasawa.github.io/)

range.location: 33
range.length: 31
url: Optional(http://d.hatena.ne.jp/niwasawa/)

======================================================================
test: [https://niwasawa.github.io/あ http://d.hatena.ne.jp/niwasawa/%E3%81%82]

range.location: 0
range.length: 28
url: Optional(https://niwasawa.github.io/%E3%81%82)

range.location: 29
range.length: 40
url: Optional(http://d.hatena.ne.jp/niwasawa/%E3%81%82)

======================================================================
test: [http://a]

range.location: 0
range.length: 8
url: Optional(http://a)

======================================================================
test: [hoge://a]

range.location: 0
range.length: 8
url: Optional(hoge://a)


考察


  • 不完全なURLも検出される

  • URLに日本語が混じっていてもパーセントエンコーディングされた結果が検出される

  • 文字列内に同じURLが存在していてもそれぞれ検出される

  • 「hellohttps://a」や「hoge://a」のようなURLも検出される

  • NSDataDetector で検出した結果について、要件に合うようにフィルタリングするのが良さそう


参考資料