Combine
프레임워크를 통해 다루기import Foundation
enum ClovaAPI {
case sentiment
var clientHeaderKeyID: String {
switch self {
case .sentiment: return "X-NCP-APIGW-API-KEY-ID"
}
}
var clientHeaderSecretID: String {
switch self {
case .sentiment: return "X-NCP-APIGW-API-KEY"
}
}
var clientHeaderContentType: String {
switch self {
case .sentiment: return "Content-Type"
}
}
var clientID: String {
switch self {
case .sentiment: // [YOUR CLIENT ID]
}
}
var clientSecret: String {
switch self {
case .sentiment: // [YOUR CLIENT SECRET]
}
}
var clientContent: String {
switch self {
case .sentiment: return "application/json"
}
}
var urlString: String {
switch self {
case .sentiment: return "https://naveropenapi.apigw.ntruss.com/sentiment-analysis/v1/analyze"
}
}
}
protocol DataService {
var sentimentAnalysisPublisher: Published<SentimentModel?>.Publisher { get }
func fetchSentimentAnalysis(_ text: String)
}
Published<[Your Model]>.Published
import SwiftUI
import Combine
class ClovaDataService: DataService {
var sentimentAnalysisPublisher: Published<SentimentModel?>.Publisher {
$sentimentAnalysis
}
@Published var sentimentAnalysis: SentimentModel? = nil
var cancellabes = Set<AnyCancellable>()
func fetchSentimentAnalysis(_ text: String) {
guard let urlRequest = getURLRequest(text) else { return }
URLSession.shared.dataTaskPublisher(for: urlRequest)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.tryMap(handleOutput)
.decode(type: SentimentModel.self, decoder: JSONDecoder())
.sink { completion in
switch completion {
case .finished:
print("SUCCESS")
break
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { [weak self] returnedData in
guard let self = self else { return }
self.sentimentAnalysis = returnedData
print(returnedData)
}
.store(in: &cancellabes)
}
private func getURL() -> URL? {
let urlString = ClovaAPI.sentiment.urlString
guard let url = URL(string: urlString) else { return nil }
return url
}
private func getURLRequest(_ body: String) -> URLRequest? {
guard let url = getURL(), let body = try? JSONSerialization.data(withJSONObject: ["content" : body]) else { return nil }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.setValue(ClovaAPI.sentiment.clientID, forHTTPHeaderField: ClovaAPI.sentiment.clientHeaderKeyID)
urlRequest.setValue(ClovaAPI.sentiment.clientSecret, forHTTPHeaderField: ClovaAPI.sentiment.clientHeaderSecretID)
urlRequest.setValue(ClovaAPI.sentiment.clientContent, forHTTPHeaderField: ClovaAPI.sentiment.clientHeaderContentType)
urlRequest.httpBody = body
return urlRequest
}
private func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
guard
let response = output.response as? HTTPURLResponse,
response.statusCode == 200 else { throw URLError(.badServerResponse) }
return output.data
}
}
Combine
프레임워크의 sink
를 통해 API를 호출한 뒤 패치된 데이터를 sentimentAnalysis
퍼블리셔 변수에 넣기import Foundation
struct SentimentModel: Codable {
let document: Document
let sentences: [Sentence]
}
// MARK: - Document
struct Document: Codable {
let sentiment: String
let confidence: Confidence
}
// MARK: - Confidence
struct Confidence: Codable {
let neutral, positive, negative: Double
}
// MARK: - Sentence
struct Sentence: Codable {
let content: String
let offset, length: Int
let sentiment: String
let confidence: Confidence
let highlights: [Highlight]
}
// MARK: - Highlight
struct Highlight: Codable {
let offset, length: Int
}
Codable
프로토콜
클로바 API가 리턴하는 감성 분석은 문자 그대로 '긍정', '중립', 부정' 수치로 이루어져 있었기 때문에 기쁨, 분노 등 별도의 보다 자세한 감정 분석을 위해서는 추가 작업이 필요해보였다.