I try to show data from API Response. I have json and DataService.
When ViewController is load, my presenter give away data from my Service.
I don't know why, but I make two request to my service. First time I get only one empty cell and the second time I get other data.
Image from view hierarchy:
My DataService:
class DataService {
func getRouts(completion: #escaping (APIResponse) -> Void?) {
let urlString = "https://travel.wildberries.ru/statistics/v1/cheap"
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
let jsonResults = try JSONDecoder().decode(APIResponse.self, from: data)
completion(jsonResults)
}
catch {
print{error}
}
}
task.resume()
}
}
My presenter
protocol PresenterProtocol: AnyObject {
func viewDidLoad()
}
final class ListTicketsModulPresenter: PresenterProtocol {
let dataService = DataService()
weak var listTicketsViewController: ListTicketsViewController?
func viewDidLoad() {
dataService.getRouts { [weak self] results in
DispatchQueue.main.async {
print(results)
self?.listTicketsViewController?.configure(with: results)
}
}
}
}
My ViewController:
final class ListTicketsViewController: UIViewController, ListTicketsViewControllerProtocol {
var presenter: PresenterProtocol
let tableView: UITableView = {
let tableView = UITableView()
tableView.backgroundColor = UIColor.white
tableView.separatorColor = .white
tableView.allowsSelection = false
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
var numberOfRows: Int?
var startCityNameArray: [String]?
var endCityNameArray: [String]?
var startDateArray: [String]?
var endDateArray: [String]?
var price: [Int]?
...
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(tableView)
setupTableView()
presenter.viewDidLoad()
}
func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(ListTicketsModulCell.self, forCellReuseIdentifier: "cellId")
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
}
//Functions
func configure(with model: APIResponse) {
self.startCityNameArray = model.data.map { $0.startCity }
self.endCityNameArray = model.data.map { $0.endCity }
self.numberOfRows = model.data.count
self.startDateArray = model.data.map { $0.startDate }
self.endDateArray = model.data.map { $0.endDate }
self.price = model.data.map { $0.price }
self.tableView.reloadData()
}
...
}
Related
I transfer data from the sharing extension to my main application with UserDefaults and open the application (goToApp()) after hitting the "post" button. However, the view of my app is not redrawn and the text remains the same "Share Extension Example". Here's how I'm trying to do it:
class ShareViewController: SLComposeServiceViewController {
private var textString: String?
override func isContentValid() -> Bool {
if let currentMessage = contentText {
self.textString = currentMessage
}
return true
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didSelectPost() {
UserDefaults.standard.set(self.textString!, forKey: "text")
gotoApp()
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func gotoApp() {
guard let url = URL(string: "example://") else { return }
let selectorOpenURL = sel_registerName("openURL:")
var responder: UIResponder? = self
while responder != nil {
if responder?.responds(to: selectorOpenURL) == true {
responder?.perform(selectorOpenURL, with: url)
}
responder = responder?.next
}
}
}
And the project to which I am trying to transfer data:
class ViewController: UIViewController {
private let mainVStack = UIStackView()
private let backgroundView = UIImageView()
private let titleLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureMainStack()
configureTitleLabel()
}
}
// MARK: - UI Elements
private extension ViewController {
func configureMainStack() {
mainVStack.distribution = .fillProportionally
mainVStack.embed(asSubviewTo: view, inset: 40)
}
func configureTitleLabel() {
titleLabel.textAlignment = .center
titleLabel.textColor = .blue
if let text = UserDefaults.object(forKey: "text") as? String {
titleLabel.text = text
} else {
titleLabel.text = "Share Extension Example"
}
let titleContainerView = UIView()
titleLabel.embedIn(titleContainerView, hInset: 0, vInset: 100)
mainVStack.addArrangedSubview(titleContainerView)
}
}
I've been practicing networking with swift 5, but I seem to always get stuck on parsing json to the UITableViewCells. I've looked at many tutorials, but can't seem to figure out the problem.
What I'm looking to do is retrieve the name of the country and place it in the tableview cells.
I use this url to request json: https://restcountries.eu/rest/v2/
The view models:
struct Countries: Codable {
let name: String?
let capital: String?
let region: String?
let population: String?
let currencies: [Currency]?
let languages: [Language]?
let flag: URL?
}
struct Currency: Codable {
let code: String?
let name: String?
let symbol: String?
}
struct Language: Codable {
let name: String?
}
My API request:
class Service {
static let shared = Service()
func fetchCountriesData(completed: #escaping (Result<Countries, ErrorMessages>) -> Void) {
let urlString = "https://restcountries.eu/rest/v2/"
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
// error
if let _ = error {
completed(.failure(.invalidData))
return
}
// response
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
// data
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
//decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode(Countries.self, from: data)
completed(.success(results))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
}
My MainViewController:
class MainViewController: UITableViewController {
var cellId = "cellId"
var countryData = [Countries]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
style()
tableView.delegate = self
tableView.dataSource = self
}
func style() {
navigationItem.title = "Search country information"
tableView.register(CountryViewCell.self, forCellReuseIdentifier: cellId)
//tableView.separatorStyle = .none
}
}
extension MainViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? CountryViewCell else {
return UITableViewCell()
}
let results = countryData[indexPath.row].name
cell.titleLabel.text = results
return cell
}
}
And the custom TableViewCell:
class CountryViewCell: UITableViewCell {
private var countryResults: Countries? {
didSet {
self.titleLabel.text = countryResults?.name
}
}
var titleLabel: UILabel = {
let label = UILabel()
label.text = "countries"
label.font = .boldSystemFont(ofSize: 16)
label.numberOfLines = 0
label.textColor = .black
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configure()
layout()
//displayResults()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension CountryViewCell {
func configure() {
selectionStyle = .gray
titleLabel.translatesAutoresizingMaskIntoConstraints = false
}
func layout() {
addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
}
A ViewController has a UICollectionView. One of the cells contains JWVideoView. The app is frequently crashing on prepareForReuse in this cell.
There is no valuable info in the log. So I am having trouble figuring out the reason for the crash.
I've created a project example that demonstrates the crash. You can find it https://github.com/fuxlud/JWExample
If the link between the cell and the videoView is removed, the crash will not happen.
import UIKit
class VideoArticleElementCollectionViewCell: UICollectionViewCell {
// MARK: - Properties
public var imageURL: String? { didSet { videoView?.imageURL = imageURL } }
public var videoId: String? { didSet { videoView?.videoId = videoId } }
#IBOutlet private var videoView: JWVideoView?
// MARK: - Reuse
override func prepareForReuse() {
super.prepareForReuse() // Crashing here! (Thread 1: EXC_BAD_ACCESS (code=1, address=0x7e8))
videoView?.stopPlayingVideo()
}
deinit {
videoView?.stopPlayingVideo()
}
}
import UIKit
class JWVideoView: UIView, JWPlayerDelegate {
// MARK: Properties
public var imageURL: String?
public var videoId: String? { didSet { setupPlayer() } }
private var jwPlayer: JWPlayerController?
private let jwPlayerURL = "https://content.jwplatform.com/manifests/"
private var didPause = false
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {}
private func setupPlayer() {
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
// configuration.premiumSkin = JWPremiumSkinGlow
configuration.image = imageURL
jwPlayer = JWPlayerController(config: configuration)
if let player = jwPlayer {
player.forceFullScreenOnLandscape = true
player.forceLandscapeOnFullScreen = true
player.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
player.view?.frame = bounds
player.delegate = self
player.volume = 0.0
if let view = player.view { addSubview(view) }
}
}
// MARK: - Orientation
private func enableAllOrientation(enable: Bool) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
// delegate.shouldEnableLandscape = enable
}
}
// MARK: API
public func stopPlayingVideo() {
enableAllOrientation(enable: false)
if jwPlayer != nil {
jwPlayer!.stop()
}
}
// MARK: - JWPlayerDelegate
internal func onFullscreen(_ status: Bool) {
if status == false {
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
}
internal func onPlayAttempt() {
if jwPlayer != nil {
enableAllOrientation(enable: true)
}
}
internal func onPlay(_ oldValue: String) {
if didPause {
didPause = false
}
}
internal func onPause(_ oldValue: String) {
didPause = true
}
internal func onComplete() {
}
}
Based on your example project a saw the following issue inside your JWVideoView class: everytime you setting the videoId property it initiliaze the jwPlayer again, and also readds this view again to the stack.
1. Solution (remove the playerView and set the player to nil):
private func setupPlayer() {
jwPlayer?.view?.removeFromSuperview()
jwPlayer = nil
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
configuration.image = imageURL
jwPlayer = JWPlayerController(config: configuration)
jwPlayer?.forceFullScreenOnLandscape = true
jwPlayer?.forceLandscapeOnFullScreen = true
jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
jwPlayer?.view?.frame = bounds
jwPlayer?.delegate = self
jwPlayer?.volume = 0.0
if let view = jwPlayer?.view {
addSubview(view)
}
}
2. Solution (keep the player and the view instance and reset the configuration of the player)
private func setupPlayer() {
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
configuration.image = imageURL
if jwPlayer == nil {
jwPlayer = JWPlayerController(config: configuration)
jwPlayer?.forceFullScreenOnLandscape = true
jwPlayer?.forceLandscapeOnFullScreen = true
jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
jwPlayer?.view?.frame = bounds
jwPlayer?.delegate = self
jwPlayer?.volume = 0.0
if let view = jwPlayer?.view {
addSubview(view)
}
}else{
//reset the configuration of the player here. but i dont now how this is possible with jwPlayer
}
}
I have data coming from Firebase and when the data is loaded I either want the an image to be hidden or shown based on some logic in my custom cell. It works perfectly fine when the data isn't being filtered but the second I type in the search bar or change the scope bar to a different index the image doesn't behave right.
For example: Index 0 should not have the image and index 1 should. Which is how it displays when it first loads. However, when I search I know the previous index 1 (now index 0) should still have it's image but it doesn't. BUT if I click to go to the detail controller it brings me to the right page. It's like it loads all the accurate info but does the logic on the original index 0. I would love some help as I have been searching for an answer FOREVER. Thank you in advance!
tableViewCell:
class SearchTalentCell: UITableViewCell {
#IBOutlet weak var userProfileImage: UIImageView!
#IBOutlet weak var talentUserName: UILabel!
#IBOutlet weak var selectedImg: UIImageView!
#IBOutlet weak var inviteSentImg: UIImageView!
var prospectRef: FIRDatabaseReference!
//#IBOutlet weak var radioButton: UIButton!
var currentTalent: UserType!
//var delegate: SearchCellDelegate?
func setTalent(talent: UserType) {
currentTalent = talent
currentTalent.userKey = talent.userKey
}
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(selectTapped))
tap.numberOfTapsRequired = 1
selectedImg.addGestureRecognizer(tap)
selectedImg.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
/*#IBAction func radioButtonTapped(_ sender: Any) {
delegate?.didTapRadioButton(userKey: currntTalent.userKey, searchSelected: currntTalent.searchSelected!.rawValue, radioButton: radioButton)
}*/
func configureCell(user: UserType, img: UIImage? = nil) {
prospectRef = Cast.REF_PRE_PRODUCTION_CASTING_POSITION.child(ProjectDetailVC.currentProject).child(FIRDataCast.prospect.rawValue).child(CastingDetailVC.positionName).child(user.userKey)
//setTalent(talent: user)
self.talentUserName.text = "\(user.firstName) \(user.lastName)"
//self.inviteSentImg.image = UIImage(named: "inviteSent")
//user.adjustSearchSelected(talent: user, radioButton: radioButton)
prospectRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.inviteSentImg.isHidden = true
print("**Image hidden")
} else {
self.inviteSentImg.image = UIImage(named: "inviteSent")
print("**Image shown")
}
})
//Image Caching
if img != nil {
self.userProfileImage.image = img
} else {
if let imageURL = user.profileImage {
let ref = FIRStorage.storage().reference(forURL: imageURL)
ref.data(withMaxSize: 2 * 1024 * 1024, completion: { (data, error) in
if error != nil {
print("ZACK: Unable to download image from Firebase Storage")
} else {
print("ZACK: Image downloaded from Firebase Storage")
if let imgData = data {
if let img = UIImage(data: imgData) {
self.userProfileImage.image = img
SearchTalentVC.userProfileImageCache.setObject(img, forKey: imageURL as NSString)
}
}
}
})
}
}
}
Viewcontroller:
class SearchTalentVC: UITableViewController/*, SearchCellDelegate*/ {
var searchingRole = [Cast]()
var unfilteredTalent = [UserType]()
var filteredTalent = [UserType]()
var selectedTalent = [UserType]()
var matchingTalentUserKeys = [String]()
var isFiltered = false
var prospectRef: FIRDatabaseReference!
static var userProfileImageCache: NSCache<NSString, UIImage> = NSCache()
let searchController = UISearchController(searchResultsController: nil)
//#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Talent"
searchController.searchBar.barStyle = .black
navigationItem.searchController = searchController
definesPresentationContext = true
searchController.searchBar.scopeButtonTitles = ["All", "Role Specific"]
searchController.searchBar.tintColor = UIColor.white
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
getTalentProfiles()
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredTalent = unfilteredTalent.filter({ (talent : UserType) -> Bool in
let doesTalentMatch = (scope == "All") || doesUserKeyMatch(talent: talent.userKey)
if searchBarIsEmpty() {
return doesTalentMatch
} else {
let fullName = "\(talent.firstName) \(talent.lastName)"
return doesTalentMatch && fullName.lowercased().contains(searchText.lowercased())
}
})
tableView.reloadData()
}
func doesUserKeyMatch(talent: String) -> Bool {
self.filterRoleFeature()
return matchingTalentUserKeys.contains(talent)
}
func isSearching() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if isSearching() {
return filteredTalent.count
} else {
return unfilteredTalent.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "userSearchCell"
if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? SearchTalentCell {
var talent: UserType
if isSearching() {
print("we are searching")
talent = self.filteredTalent[indexPath.row]
print("indexPath: \(indexPath.row)")
} else {
print("we are not searching")
talent = self.unfilteredTalent[indexPath.row]
}
if let imageURL = talent.profileImage {
if let img = SearchTalentVC.userProfileImageCache.object(forKey: imageURL as NSString) {
cell.configureCell(user: talent, img: img)
} else {
cell.configureCell(user: talent)
//cell.delegate = self
}
return cell
} else {
cell.configureCell(user: talent)
//cell.delegate = self
return SearchTalentCell()
}
} else {
return SearchTalentCell()
}
}
extension SearchTalentVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
self.tableView.reloadData()
}
}
extension SearchTalentVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
If you are hidding image in If part then you should put logic of showing image also in else block. See if this solve your problem.
if let _ = snapshot.value as? NSNull {
self.inviteSentImg.isHidden = true
print("**Image hidden")
} else {
self.inviteSentImg.isHidden = false
self.inviteSentImg.image = UIImage(named: "inviteSent")
print("**Image shown")
}
In Root view, I used Tabbar controller and there are 4 tabs.
At first tab View(index = 0), user can search books on open Book API services. It works well, but here is a problem.
1. Users search a book on first tab view (index = 0,tableview)
2. The results come out
3. Users tab other tab button and move other views.
4. Users tab a first button(index=0,table view) for backing to search other books.
5. The Black screen shows up in first tab view, but the user can move to other views by tapping other tabs, there are no black screens. There is a black screen only in the first view(index=0)
What's the problem with my app?
I coded my app with swift.
import Foundation
import UIKit
class SearchHome: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate{
// MARK: - Properties
let searchController = UISearchController(searchResultsController: nil)
// var barButton = UIBarButtonItem(title: "search", style: .Plain, target: nil, action: nil)
let apiKey : String = "cbccaa3f----d980b0c"
var searchString : String = ""
var list = Array<BookAPIresult>()
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
self.searchController.searchBar.text! = ""
// Setup the Search Controller
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.searchBarStyle = UISearchBarStyle.Prominent
searchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = searchController.searchBar
// Setup the Scope Bar
searchController.searchBar.scopeButtonTitles = ["title", "hashtag"]
searchController.searchBar.placeholder = "book search"
// Setup Animation for NavigationBar
navigationController?.hidesBarsOnSwipe = true
searchController.hidesNavigationBarDuringPresentation = false
navigationController?.hidesBarsWhenKeyboardAppears = false
navigationController?.hidesBarsOnTap = true
navigationController?.hidesBarsWhenVerticallyCompact = true
self.refreshControl?.addTarget(self, action: #selector(SearchHome.handleRefresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
// declare hide keyboard tap
let hideTap = UITapGestureRecognizer(target: self, action: #selector(SearchHome.hideKeyboardTap(_:)))
hideTap.numberOfTapsRequired = 1
self.view.userInteractionEnabled = true
self.view.addGestureRecognizer(hideTap)
// declare hide keyboard swipe
let hideSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SearchHome.hideKeyboardSwipe(_:)))
self.view.addGestureRecognizer(hideSwipe)
}
// MARK: - UISearchBar Delegate
func searchBar(searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
// filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar){
self.searchString = self.searchController.searchBar.text!
self.list.removeAll()
self.callBookAPI()
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = self.list[indexPath.row]
NSLog("title:\(row.title),row:\(indexPath.row), author:\(row.author)")
let cell = tableView.dequeueReusableCellWithIdentifier("ListCell") as! BookAPIResultCell
cell.title?.text = row.title
cell.author?.text = row.author
dispatch_async(dispatch_get_main_queue(),{ cell.thumb.image = self.getThumbnailImage(indexPath.row)})
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
NSLog("%d",indexPath.row)
}
override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
self.view.endEditing(false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func callBookAPI(){
let encodedSearchString = searchString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let apiURI = NSURL(string: "https://apis.daum.net/search/book?apikey=\(self.apiKey)&q=\(encodedSearchString!)&searchType=title&output=json")
let apidata : NSData? = NSData(contentsOfURL: apiURI!)
NSLog("API Result = %#", NSString(data: apidata!, encoding: NSUTF8StringEncoding)!)
do {
let data = try NSJSONSerialization.JSONObjectWithData(apidata!, options:[]) as! NSDictionary
let channel = data["channel"] as! NSDictionary
// NSLog("\(data)")
let result = channel["item"] as! NSArray
var book : BookAPIresult
for row in result {
book = BookAPIresult()
let title = row["title"] as? String
let decodedTitle = title?.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let titleFromHTML = decodedTitle!.html2String
let titleRemoveB = titleFromHTML
let titleRemoveBEnd = titleRemoveB.stringByReplacingOccurrencesOfString("</b>", withString: "")
book.title = titleRemoveBEnd
if let authorEx = row["author"] as? String{
book.author = authorEx
}else{
book.author = ""
}
book.thumbnail = row["cover_s_url"] as? String
//NSLog("\(book.thumbnail)")
let description = row["description"] as? String
let decodedDescription = description?.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
book.description = decodedDescription!
self.list.append(book)
}
} catch {
NSLog("parse error")
}
}
func getThumbnailImage(index : Int) -> UIImage {
let book = self.list[index]
if let savedImage = book.thumbnailImage {
return savedImage
} else {
if book.thumbnail == "" {
book.thumbnailImage = UIImage(named:
"Book Shelf-48.png")
}else{
let url = NSURL(string: book.thumbnail!)
let imageData = NSData(contentsOfURL: url!)
book.thumbnailImage = UIImage(data:imageData!)
}
return book.thumbnailImage!
}
}
func handleRefresh(refreshControl:UIRefreshControl){
self.searchString = self.searchController.searchBar.text!
self.list.removeAll()
self.callBookAPI()
self.tableView.reloadData()
refreshControl.endRefreshing()
}
// hide keyboard if tapped
func hideKeyboardTap(recognizer: UITapGestureRecognizer) {
self.view.endEditing(true)
}
// hide keyboard if swipe
func hideKeyboardSwipe(recognizer: UISwipeGestureRecognizer) {
self.view.endEditing(true)
}
override func prefersStatusBarHidden() -> Bool {
return false
}
}
extension String {
var html2AttributedString: NSAttributedString? {
guard
let data = dataUsingEncoding(NSUTF8StringEncoding)
else { return nil }
do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute:NSUTF8StringEncoding], documentAttributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
return nil
}
}
var html2String: String {
return html2AttributedString?.string ?? ""
}
}
extension SearchHome: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
// filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}