출처 http://qiita.com/fumiyasac@github/items/02a7b962e9a2013c56a0
시작하기:Swift1.1 → Swift1.2 → Swift2.x에서 변경사항이 있는 NSXMLParser
현재는 API로부터 정보를 가져와서 UITableView에 표시하기 위한 처리를 하는 경우 Response는 JSON으로 하는 경우가 많이 있다고 생각됩니다. Swift2.0에서도 XML의 해석 및 읽기 처리를 할 수 있습니다.(공개된 API는 거의 두가지 형식을 다 지원하는 경우가 많다)
주의점:parser 메소드의 attributes attributeDict:가 버젼마다 다릅니다.
parser메소드에 대해 특별히 크게 변경 된 점이 있는 것은 아래에 기술하였습니다.
또 그것을 처리하거나 기타 parser메소드에 대해서도 optional의 위치도 미묘하게 차이가 있으니 주의할 필요가 있습니다.
Swift1.1)
xxx.swift
parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: NSDictionary!)
↑attributeDictはNSDictonary!
Swift1.2)
xxx.swift
parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!)
↑attributeDictは[NSObject : AnyObject]!
Swift2.x)
xxx.swift
parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [String : String])
↑attributeDictは[String : String]
이런 이유로 각각의 버젼별 차이점이 있기 때문에 처리도 각각 해줘야 합니다. Swift2.0에서 XML를 해석하는 포인트를 깔끔하게 정리할려고 합니다.
코드:실제 처리에서 포인트가 되는 부분은 이것입니다.
■ 사용API:
이 예제는「과자의 포로」API를 이용합니다.
http://www.sysbird.jp/toriko/webapi/
- 실제 이 API의 Response에 관해서는「과자의 포로」의 페이지를 참조해주세요
- 이번에는 상기 API를 사용해서 랜덤 데이터를 30개 취득하는 것을 가정으로 기술되었습니다.
- UITableView에 표시하는 내용은 아래 5개 입니다.
- 과자 이름
- 과자 썸네일
- 과자 메이커 이름
- 과자 카테고리
- 과자 가격
1. Delegate또는 변수의 준비
ViewController.swift(class선언부분)
//NSXMLParserDelegate를 기술
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSXMLParserDelegate
2. 멤버변수 또는 분석데이터를 저장하는 변수 준비
ViewController.swift(프로퍼티의 선언부분)
//XMLURL
let feedUrl : NSURL = NSURL(string:"http://www.sysbird.jp/webapi/?apikey=guest&max=30&order=r")!
//XML현재요소명 변수
var currentElementName : String!
//취득한 요소명(시작요소명)
let itemElementName : String = "item"
//취득한 요소명 확정(item요소는 다음과 같음)
let nameElementName : String = "name"
let makerElementName : String = "maker"
let priceElementName : String = "price"
let typeElementName : String = "type"
let urlElementName : String = "url"
let imageElementName : String = "image"
//각 엘리멘트용 변수
var posts = NSMutableArray()
var elements = NSMutableDictionary()
var element = NSString()
var name = NSMutableString()
var maker = NSMutableString()
var price = NSMutableString()
var type = NSMutableString()
var url = NSMutableString()
var image = NSMutableString()
3. viewDidLoad 처리
ViewController.swift(viewDidLoad内)
override func viewDidLoad() {
super.viewDidLoad()
//해석데이터 저장용
posts = []
//UITableView에 관한 처리를 기술
...(중략)...
//NSXMLParser클래스의 인스탄스 준비
let parser : NSXMLParser = NSXMLParser(contentsOfURL: feedUrl)!
//XML파서 delegate
parser.delegate = self
//XML파서 실행
parser.parse()
}
4. XML파서 처리실행개시시 처리
ViewController.swift(NSXMLParser)
//이번에 특별한 것이 없으니 공란 그대로
func parserDidStartDocument(parser: NSXMLParser) {
}
5. XML파서 처리실행중에 수행할 작업(NSXMLParser)
item요소를 찾음
※XML의요소를 상단에서부터 조사해서 이미지를 처리한다.
ViewController.swift(viewDidLoad内)
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
self.element = elementName
//요소명이「item」 의요소가 보인 경우에는 위세 설정한 멤버변수를 초기화한다.
if (elementName as NSString).isEqualToString(self.itemElementName){
self.elements = [:]
self.name = ""
self.maker = ""
self.price = ""
self.type = ""
self.url = ""
self.image = ""
}
}
6. XML파서 처리실행중에 수행할 작업(NSXMLParser)
item요소내에서 name・url・price・maker・url・image요소를 찾아 item요소를 발견했을 때 준비한 변수에 담아준다.
ViewController.swift(NSXMLParser)
func parser(parser: NSXMLParser, foundCharacters string: String){
if self.element.isEqualToString(self.nameElementName) {
self.name.appendString(
strip(string)
)
}
if self.element.isEqualToString(self.makerElementName) {
self.maker.appendString(
strip(string)
)
}
if self.element.isEqualToString(self.priceElementName) {
self.price.appendString(
strip(string)
)
}
if self.element.isEqualToString(self.typeElementName) {
self.type.appendString(
strip(string)
)
}
if self.element.isEqualToString(self.urlElementName) {
self.url.appendString(
strip(string)
)
}
if self.element.isEqualToString(self.imageElementName) {
self.image.appendString(
strip(string)
)
}
}
이대로라면 XML의 요소를 취득 할 때 여분의 줄 바꿈과 공백이 포함되어 버리므로, 줄 바꿈과 공백을 제거하는 방법도 추가해야 합니다.
ViewController.swift(NSXMLParser)
//줄 바꿈과 공백을 제거한다.
func strip(str: String) -> String {
var strBr: String
var strSp: String
//줄 바꿈 제거
strBr = str.stringByReplacingOccurrencesOfString("\n", withString: "", options: [], range: nil)
//공백 제거
strSp = strBr.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)
return strSp
}
7. XML파서 처리실행중에 수행할 작업(NSXMLParser)
태그의 마지막을 검출했을 때 꺼낸 요소에 부합한 경우에는 해석 데이터 저장용에 넣는다.
ViewController.swift(NSXMLParser)
func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if (elementName as NSString).isEqualToString(self.itemElementName) {
//각 멤버변수가 nil이 아니면 멤버변수 elements에 '키'와 '값'을 쌍으로 저장한다.
if !self.name.isEqual(nil) {
self.elements.setObject(self.name, forKey: self.nameElementName)
}
if !self.maker.isEqual(nil) {
self.elements.setObject(self.maker, forKey: self.makerElementName)
}
if !self.price.isEqual(nil) {
self.elements.setObject(self.price, forKey: self.priceElementName)
}
if !self.type.isEqual(nil) {
self.elements.setObject(self.type, forKey: self.typeElementName)
}
if !self.url.isEqual(nil) {
self.elements.setObject(self.url, forKey: self.urlElementName)
}
if !self.image.isEqual(nil) {
self.elements.setObject(self.image, forKey: self.imageElementName)
}
self.posts.addObject(self.elements)
}
}
마지막으로. Swift 버젼업 공식문서를 확인하자.
소스적으로는 깨끗하다고 말할 수 없습니다만, XML의 해석처리 부분을 작성할 때 조금이나마 도움이 되었으면 하는 바램입니다.
이미지를 취득하는 처리는 이정도 하는게 좋지 않을까?
ViewController.swift(viewDidLoad内)
//화상 이미지요소의 데이터를 취득한 후에 nil 일 가능성을 체크한다.
let q_global: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
let q_main: dispatch_queue_t = dispatch_get_main_queue();
let imageParameter: String! = (posts.objectAtIndex(indexPath.row).valueForKey("image")!.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding))
//URL이 있다면 화상취득처리를 한다.
if imageParameter != "" {
//썸네일URL을 바탕을로 화상데이터(NSData형)을 작성
let imageURL = NSURL(string: imageParameter)
//비동기로 URL데이터를 취득
dispatch_async(q_global,{
//썸네일URL을 바탕을로 화상데이터(NSData형)을 작성
var error: NSError?
var imageData: NSData?
//데이터를 취득 할수 있으면 정상처리
do {
imageData = try NSData(contentsOfURL: imageURL!, options: [])
//Error가 돌아온 경우는 imageData에 nil을 넣어준다.
} catch let error1 as NSError {
error = error1
imageData = nil
//그거 이외는 예와
} catch {
fatalError()
}
if error != nil {
//nil의 경우 디폴트 이미지를 표시한다.
let image: UIImage = UIImage(named: "no_image.gif")!
cell!.okashiImage?.image = image
}
//갱신은 메인쓰레드에서 한다.
dispatch_async(q_main,{
//이미지데이터가 nil이 아니라면 썸네일 이미지를 표시
if((imageData) != nil){
//xib의 썸네일영역을 표시한다.
let image: UIImage = UIImage(data: imageData!)!
cell!.okashiImage?.image = image
cell!.layoutSubviews()
}
})
})
} else {
//nil의 경우는 디폴트 이미지를 표시한다.
let image: UIImage = UIImage(named: "no_image.gif")!
cell!.okashiImage?.image = image
}
실제 동작하는 소스:
https://github.com/fumiyasac/XMLFeedSample