Kakao Book Search
import UIKit
import SwiftyJSON
import Alamofire
import Kingfisher
struct Book {
let authors: String
let datetime: String
let price: Int
let publisher: String
let salePrice: Int
let thumbnail: String
let title: String
var content1: String {
return "\(title)"
}
var content2: String {
return "\(authors) | \(publisher)"
}
var content3: String {
return datetime
}
var content4: String {
if (salePrice == -1 ) {
return "절판"
}
else {
let dcPercent = lround( ( (Double(price) - Double(salePrice) ) / Double(price) ) * 100)
return "\(price) -> \(salePrice) ( \(dcPercent) % 할인 )"
}
}
}
enum sorting: String {
case accuracy
case latest
var forMenu: String {
switch self {
case .accuracy : return "정확도순"
case .latest : return "발간일순"
}
}
}
enum targeting: String {
case all
case title
case publisher
case person
var forMenu: String {
switch self {
case .all : return "전체"
case .title : return "제목"
case .publisher : return "출판사"
case .person : return "인명"
}
}
}
class KakaoBookViewController: UIViewController {
@IBOutlet var searchBar: UISearchBar!
@IBOutlet var bookTableView: UITableView!
@IBOutlet var targetPullDownButton: UIButton!
@IBOutlet var sortPullDownButton: UIButton!
var bookList: [Book] = []
var pageCnt = 1
var isEnd = false
var sortingOption = sorting.accuracy
var targetingOption = targeting.all
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
bookTableView.dataSource = self
bookTableView.delegate = self
bookTableView.prefetchDataSource = self
bookTableView.rowHeight = 120
designSorting(sortPullDownButton)
designTargeting(targetPullDownButton)
}
func callRequest(_ query: String, _ pageCnt: Int) {
guard let txt = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
var target = ""
if (targetingOption != .all) {
target = "&target=\(targetingOption.rawValue)"
}
let sort = "&sort=\(sortingOption.rawValue)"
let url = "https://dapi.kakao.com/v3/search/book?query=\(txt)&size=10&page=\(pageCnt)\(target)\(sort)"
let header: HTTPHeaders = ["Authorization" : APIKey.kakao]
AF.request(url, method: .get, headers: header)
.validate(statusCode: 200...500)
.responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
let statusCode = response.response?.statusCode ?? 500
if (statusCode == 200) {
self.isEnd = json["meta"]["is_end"].boolValue
for book in json["documents"].arrayValue {
let authors = book["authors"][0].stringValue
let datetime = book["datetime"].stringValue
let price = book["price"].intValue
let publisher = book["publisher"].stringValue
let salePrice = book["sale_price"].intValue
let thumbnail = book["thumbnail"].stringValue
let title = book["title"].stringValue
let newBook = Book(authors: authors, datetime: datetime, price: price, publisher: publisher, salePrice: salePrice, thumbnail: thumbnail, title: title)
self.bookList.append(newBook)
}
self.bookTableView.reloadData();
}
else {
print("에러났어용")
print(json)
}
case .failure(let error):
print(error)
}
}
}
func designSorting(_ sender: UIButton) {
sender.setTitle(sortingOption.forMenu, for: .normal)
let op1 = UIAction(title: sorting.accuracy.forMenu) { _ in
self.sortingOption = sorting.accuracy
sender.setTitle(self.sortingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let op2 = UIAction(title: sorting.latest.forMenu) { _ in
self.sortingOption = sorting.latest
sender.setTitle(self.sortingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let buttonMenu = UIMenu(title: "정렬 기준을 고르세요", children: [op1, op2])
sender.menu = buttonMenu
}
func designTargeting(_ sender: UIButton) {
sender.setTitle(targetingOption.forMenu, for: .normal)
let op1 = UIAction(title: targeting.all.forMenu) { _ in
self.targetingOption = targeting.all
sender.setTitle(self.targetingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let op2 = UIAction(title: targeting.title.forMenu) { _ in
self.targetingOption = targeting.title
sender.setTitle(self.targetingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let op3 = UIAction(title: targeting.publisher.forMenu) { _ in
self.targetingOption = targeting.publisher
sender.setTitle(self.targetingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let op4 = UIAction(title: targeting.person.forMenu) { _ in
self.targetingOption = targeting.person
sender.setTitle(self.targetingOption.forMenu, for: .normal)
self.pageCnt = 1
self.bookList.removeAll()
self.callRequest(self.searchBar.text!, self.pageCnt)
}
let buttonMenu = UIMenu(title: "검색 필드를 고르세요", children: [op1, op2, op3, op4])
sender.menu = buttonMenu
}
}
extension KakaoBookViewController: UITableViewDelegate, UITableViewDataSource, UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bookList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "KakaoBookTableViewCell") as! KakaoBookTableViewCell
if let url = URL(string: bookList[indexPath.row].thumbnail) {
cell.mainImageView.kf.setImage(with: url)
}
cell.firstLabel.text = bookList[indexPath.row].content1
cell.secondLabel.text = bookList[indexPath.row].content2
cell.thirdLabel.text = bookList[indexPath.row].content3
cell.fourthLabel.text = bookList[indexPath.row].content4
return cell
}
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
print("===== 프리패치 \(indexPaths) =====")
for indexPath in indexPaths {
if (bookList.count-1 == indexPath.row &&
pageCnt < 15 &&
!isEnd ) {
pageCnt += 1
callRequest(searchBar.text!, pageCnt)
}
}
}
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
print("===== 취소여 \(indexPaths) =====")
}
}
extension KakaoBookViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
pageCnt = 1
bookList.removeAll()
guard let query = searchBar.text else {return }
if query == "" {
bookTableView.reloadData()
}
else {
callRequest(searchBar.text!, pageCnt)
}
}
}
Papago Translator
import UIKit
import SwiftyJSON
import Alamofire
class Translate2ViewController: UIViewController {
@IBOutlet var firstTextField: UITextField!
@IBOutlet var secondTextField: UITextField!
@IBOutlet var firstTextView: UITextView!
@IBOutlet var secondTextView: UITextView!
@IBOutlet var translateButton: UIButton!
let firstPickerView = UIPickerView()
let secondPickerView = UIPickerView()
let dict = [
"Korean": "ko",
"English": "en",
"Japanese": "ja",
"Chinese - CN": "zh-CN",
"Chinese - TW": "zh-TW",
"Vietnam": "vi",
"Indonesia": "id",
"Thailand": "th",
"German": "de",
"Russian": "ru",
"Spanish": "es",
"Italy": "it",
"Franch": "fr"
]
override func viewDidLoad() {
super.viewDidLoad()
design(firstTextField)
design(secondTextField)
design(firstTextView)
design(secondTextView)
design(translateButton)
translateButton.setTitle("Translate!", for: .normal)
firstPickerView.delegate = self
firstPickerView.dataSource = self
secondPickerView.delegate = self
secondPickerView.dataSource = self
firstTextField.inputView = firstPickerView
secondTextField.inputView = secondPickerView
firstTextField.placeholder = "언어를 선택하세요"
secondTextField.placeholder = "언어를 선택하세요"
firstTextView.text = ""
secondTextView.text = ""
secondTextView.isEditable = false
}
func design(_ sender: UIView) {
sender.layer.borderWidth = 1
}
@IBAction func tranlateButtonTapped(_ sender: UIButton) {
if ( firstTextField.text == "" || secondTextField.text == "") {
return;
}
let url = "https://openapi.naver.com/v1/papago/n2mt"
let header: HTTPHeaders = [
"X-Naver-Client-Id" : APIKey.naver,
"X-Naver-Client-Secret" : APIKey.naverSecret
]
let parameter: Parameters = [
"source": dict[firstTextField.text!]!,
"target": dict[secondTextField.text!]!,
"text": firstTextView.text!
]
AF.request(url, method: .post, parameters: parameter, headers: header)
.validate()
.responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
self.secondTextView.text = json["message"]["result"]["translatedText"].stringValue
case .failure(let error):
print(error)
}
}
}
@IBAction func tapgesture(_ sender: UITapGestureRecognizer) {
view.endEditing(true)
}
}
extension Translate2ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
dict.keys.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if (pickerView == firstPickerView) {
firstTextField.text = String( Array(dict.keys)[row] )
} else {
secondTextField.text = String( Array(dict.keys)[row] )
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return String( Array(dict.keys)[row] )
}
}
TMDB Movie Search
URL + Extension
import Foundation
extension URL {
static let baseURL = "https://api.themoviedb.org/3/"
static func makeEndPointString(_ endpoint: String) -> String {
return baseURL + endpoint
}
}
enum Endpoint {
case movieTrend
case movieGenre
case movieDetail
case imagePrefix
var requestURL: String {
switch self {
case .movieTrend : return URL.makeEndPointString("trending/movie/week?language=en-US")
case .movieGenre : return URL.makeEndPointString("genre/movie/list")
case .movieDetail : return URL.makeEndPointString("movie/")
case .imagePrefix : return "https://image.tmdb.org/t/p/w500/"
}
}
}
APIManager
import Foundation
import SwiftyJSON
import Alamofire
class APIManager {
static let shared = APIManager()
private init() {}
let header: HTTPHeaders = ["Authorization" : APIKey.tmdb]
func callRequest(_ type: Endpoint, _ movieID: Int, completionHandler: @escaping (JSON) -> () ) {
var url = type.requestURL
if (type == .movieDetail) {
url += "\(movieID)/credits"
}
AF.request(url, method: .get, headers: header)
.validate()
.responseJSON { response in
switch response.result {
case .success(let value) :
let json = JSON(value)
completionHandler(json)
case .failure(let error) :
print(error)
}
}
}
}
MainViewController
import UIKit
import SwiftyJSON
import Alamofire
class MainViewController: UIViewController {
var movieList: [MovieForMain] = []
@IBOutlet var mainTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
mainTableView.delegate = self
mainTableView.dataSource = self
let nib = UINib(nibName: "MovieTableViewCell", bundle: nil)
mainTableView.register(nib, forCellReuseIdentifier: "MovieTableViewCell")
mainTableView.rowHeight = 400
callRequest(callRequest2)
}
func callRequest2() {
APIManager.shared.callRequest(.movieGenre, 0) { json in
for (index, movie) in self.movieList.enumerated() {
print(index, movie.genre)
self.movieList[index].genreString.removeAll()
for movieGenre in movie.genre {
for g in json["genres"].arrayValue {
if g["id"].intValue == movieGenre {
self.movieList[index].genreString.append(g["name"].stringValue)
}
print(index, self.movieList[index].genreString)
}
}
}
self.mainTableView.reloadData()
}
}
func callRequest(_ completion: @escaping () -> Void) {
APIManager.shared.callRequest(.movieTrend, 0) { json in
for item in json["results"].arrayValue {
print(item)
let date = item["release_date"].stringValue
var genre: [Int] = []
for g in item["genre_ids"].arrayValue {
genre.append(g.intValue)
}
let mainImage = item["poster_path"].stringValue
let backImage = item["backdrop_path"].stringValue
let rate = item["vote_average"].doubleValue
let title = item["title"].stringValue
let id = item["id"].intValue
let overView = item["overview"].stringValue
let newMovie = MovieForMain(id: id, date: date, genre: genre, genreString: [], mainImage: mainImage, backImage: backImage, rate: rate, title: title, overview: overView)
self.movieList.append(newMovie)
}
completion()
self.mainTableView.reloadData()
}
}
}
extension MainViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell") as! MovieTableViewCell
cell.designCell(movieList[indexPath.row])
return cell;
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
vc.movieID = movieList[indexPath.row].id
vc.movieName = movieList[indexPath.row].title
vc.overView = movieList[indexPath.row].overview
vc.mainImageLink = movieList[indexPath.row].mainImage
vc.backImageLink = movieList[indexPath.row].backImage
tableView.reloadRows(at: [indexPath], with: .automatic)
navigationController?.pushViewController(vc, animated: true)
}
}
DetailViewController
import UIKit
import Alamofire
import SwiftyJSON
class DetailViewController: UIViewController {
@IBOutlet var titleLabel: UILabel!
@IBOutlet var backImageView: UIImageView!
@IBOutlet var mainImageView: UIImageView!
@IBOutlet var overTextView: UITextView!
@IBOutlet var castTextView: UITextView!
@IBOutlet var crewTextView: UITextView!
var movieID: Int = 0
var movieName: String = ""
var overView: String = ""
var mainImageLink: String = ""
var backImageLink: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print(movieID)
titleLabel.text = movieName
overTextView.text = overView
castTextView.text = ""
crewTextView.text = ""
overTextView.isEditable = false
castTextView.isEditable = false
crewTextView.isEditable = false
let urlMain = URL(string: Endpoint.imagePrefix.requestURL + mainImageLink)
mainImageView.kf.setImage(with: urlMain)
let urlBack = URL(string: Endpoint.imagePrefix.requestURL + backImageLink)
backImageView.kf.setImage(with: urlBack)
backImageView.contentMode = .scaleAspectFill
APIManager.shared.callRequest(.movieDetail, movieID) { json in
for person in json["cast"].arrayValue {
self.castTextView.text += person["name"].stringValue
self.castTextView.text += "\n"
}
for person in json["crew"].arrayValue {
self.crewTextView.text += person["name"].stringValue
self.crewTextView.text += "\n"
}
}
}
}