Image gallery not show after request access permission - swift

i try to show image gallery after user permission to allow all photos, but the gallery is not showing. but when i back to previous controller and navigate back, the gallery show up. but that's not what i want, i want after user allow the image show up.
this my setup
private var allPhotos: [PHAsset] = []
override func viewDidLoad() {
super.viewDidLoad()
PHPhotoLibrary.shared().register(self)
setupCollectionView()
checkPhotoLibraryPermission()
bindViewModel()
}
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
private func bindViewModel() {
let dataSource = Observable.just(allPhotos)
dataSource.asObservable()
.bind(to: collectionView.rx.items(cellIdentifier: GalleryCollectionViewCell.cellId, cellType: GalleryCollectionViewCell.self)) { row, asset, cell in
let imageRequestOptions = PHImageRequestOptions()
imageRequestOptions.resizeMode = .exact
self.imageManager.requestImageDataAndOrientation(for: asset, options: imageRequestOptions) { imageData, _, orientation, info in
guard let imageData = imageData else { return }
cell.setup(imageData: imageData)
}
}.disposed(by: disposedBag)
collectionView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
guard let strongSelf = self else { return }
let asset = strongSelf.allPhotos[indexPath.row]
asset.requestContentEditingInput(with: PHContentEditingInputRequestOptions()) { editingInput, info in
guard let path = editingInput?.fullSizeImageURL?.path.replacingOccurrences(of: "HEIC", with: "PNG") else { return }
self?.imageManager.requestImageDataAndOrientation(for: asset, options: self?.imageRequestOptions) { imageData, _, orientation, info in
guard let imageData = imageData else { return }
self?.goToCropImage(from: imageData, and: path.lastPathComponent)
}
}
}).disposed(by: disposedBag)
}
private func fetchAllPhotos() {
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
allPhotos = fetchResult.objects(at: IndexSet(0..<fetchResult.count))
}
private func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
fetchAllPhotos()
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
case .denied, .restricted :
//handle denied status
gotoAppSettings()
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
self.fetchAllPhotos()
case .denied, .restricted:
// as above
self.gotoAppSettings()
case .notDetermined:
// won't happen but still
break
case .limited:
break
#unknown default:
fatalError("Failed to get user permission to access photo")
}
}
case .limited:
fetchAllPhotos()
#unknown default:
fatalError("Failed to get user permission to access photo")
}
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
let allPhotosOptions = PHFetchOptions()
let fetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
DispatchQueue.main.async {
self.allPhotos = fetchResult.objects(at: IndexSet(0..<fetchResult.count))
self.collectionView.reloadData()
}
}
I already try to to reload collectionView but it still not show up.

The way that UICollectionView.rx.items works is that it observes its dataSource. When the dataSource emits a new array, the items operator will reload the collection view and call its closure for each item.
Since you are using just as your data source, only one array is emitted and the collection view never changes. You have to tie the source to the change observer to get it to work. Here is a working example:
extension PhotosViewController { // a UICollectionViewController
func connect(disposeBag: DisposeBag) {
// initial fetch result
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let initialFetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
let assets = PHPhotoLibrary.shared().rx.registerChangeObserver()
// when a change is observed, we need to update the fetchResult
.scan(initialFetchResult) { oldResult, change in
guard let changes = change.changeDetails(for: oldResult) else { return oldResult }
return changes.fetchResultAfterChanges
}
// but first send the initial asset fetch to the collection view
.startWith(initialFetchResult)
// and get the assets out of the fetch result.
.map { $0.objects(at: IndexSet(0 ..< $0.count)) }
collectionView.dataSource = nil
assets
.observe(on: MainScheduler.instance)
.bind(to: collectionView.rx.items(cellIdentifier: "GridViewCell", cellType: GridViewCell.self)) { _, asset, cell in
cell.configure(asset: asset)
}
.disposed(by: disposeBag)
}
}
extension Reactive where Base: PHPhotoLibrary {
// not actually needed, but I provided it as an example.
static func requestAuthorization() -> Observable<PHAuthorizationStatus> {
Observable.create { observer in
Base.requestAuthorization { status in
observer.onNext(status)
observer.onCompleted()
}
return Disposables.create()
}
}
// this sets up the change observer. Note, your VC isn't the observer.
func registerChangeObserver() -> Observable<PHChange> {
Observable.create { [base] observer in
let changeObserver: RxPhotoLibraryChangeObserver = .init(observer: observer)
base.register(changeObserver)
return Disposables.create { base.unregisterChangeObserver(changeObserver) }
}
}
}
// this is the change observer used in the above.
final class RxPhotoLibraryChangeObserver: NSObject, PHPhotoLibraryChangeObserver {
let observer: AnyObserver<PHChange>
init(observer: AnyObserver<PHChange>) {
self.observer = observer
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
observer.onNext(changeInstance)
}
}

Related

How do I handle web socket re-connect for .cancelled case in Starscream pod?

I have a small iOS app in Swift which fetches data on stock prices from a public API using the Starscream swift pod running on an iPhone Pro 12. The app downloads, processes and displays the stock data correctly when wifi is connected. However, if I swipe up from the bottom of the device, the app screen disappears into the app icon and my debug window reports that sceneDidResignActive followed by sceneDidEnterBackground are called. The debug console also reports that the .cancelled case is being sent to the didReceive(event:client:) delegate method of StarScream. When I swipe up from the bottom of the device again and select the app screen the scene delegate methods sceneWillEnterForeground and sceneDidBecomeActive are called but my app is no longer updating and displaying live stock price information. The socket property is still there but is not connected. I have tried placing the line self.socket.connect() in the .cancelled switch case but it stops my app from handling wifi disconnection correctly. What code should I have in my scene delegate file or in .cancelled switch case to get the app to continue downloading and displaying stock price information from the API?
Here is my NetworkServices class;
//
// Services.swift
// BetVictorTask
//
// Created by Stephen Learmonth on 28/02/2022.
//
import UIKit
import Starscream
import Network
protocol NetworkServicesDelegate: AnyObject {
func sendStockInfo(stocksInfo: [String: StockInfo])
}
final class NetworkServices {
static let sharedInstance = NetworkServices()
var request = URLRequest(url: FINNHUB_SOCKET_STOCK_INFO_URL!)
var socket: WebSocket!
public private(set) var isConnected = false
var stocksInfo: [String: StockInfo] = [:]
var socketResults: [String: [StockInfo]] = [:]
weak var delegate: NetworkServicesDelegate?
var stockSymbols: [String] = []
private init() {
request.timeoutInterval = 5
socket = WebSocket(request: request)
socket.delegate = self
}
private let queue = DispatchQueue.global()
private let monitor = NWPathMonitor()
public func startMonitoring() {
monitor.start(queue: queue)
self.monitor.pathUpdateHandler = { [weak self] path in
if path.status == .satisfied {
// connect the socket
self?.socket.connect()
print("DEBUG: socket is connected")
} else {
self?.socket.disconnect()
print("DEBUG: socket is disconnected")
self?.isConnected = false
// post notification that socket is now disconnected
DispatchQueue.main.async {
print("DEBUG: Notification \"isDisconnected\" posted")
let name = Notification.Name(rawValue: isDisconnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
}
}
}
}
public func stopMonitoring() {
monitor.cancel()
socket.disconnect()
isConnected = false
}
func fetchStockInfo(symbols: [String], delegate: CompanyPriceListVC) {
stockSymbols = symbols
self.delegate = delegate
for symbol in symbols {
let string = FINNHUB_SOCKET_MESSAGE_STRING + symbol + "\"}"
socket.write(string: string)
}
}
private func parseJSONSocketData(_ socketString: String) {
self.socketResults = [:]
self.stocksInfo = [:]
let decoder = JSONDecoder()
do {
let socketData = try decoder.decode(SocketData.self, from: socketString.data(using: .utf8)!)
guard let stockInfoData = socketData.data else { return }
for stockInfo in stockInfoData {
let symbol = stockInfo.symbol
if self.socketResults[symbol] == nil {
self.socketResults[symbol] = [StockInfo]()
}
self.socketResults[symbol]?.append(stockInfo)
}
for (symbol, stocks) in self.socketResults {
for item in stocks {
if self.stocksInfo[symbol] == nil {
self.stocksInfo[symbol] = item
} else if item.timestamp > self.stocksInfo[symbol]!.timestamp {
self.stocksInfo[symbol] = item
}
}
}
self.delegate?.sendStockInfo(stocksInfo: self.stocksInfo)
} catch {
print("DEBUG: error: \(error.localizedDescription)")
}
}
func fetchCompanyDetails(symbol: String, completion: #escaping (CompanyInfo?, UIImage?)->()) {
let urlString = FINNHUB_HTTP_COMPANY_INFO_URL_STRING + symbol + "&token=" + FINNHUB_API_TOKEN
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error fetching company info: \(error)")
}
guard let data = data else { return }
let decoder = JSONDecoder()
do {
let companyInfo = try decoder.decode(CompanyInfo.self, from: data)
guard let logoURL = URL(string: companyInfo.logo) else { return }
let task = URLSession.shared.dataTask(with: logoURL) { data, response, error in
if let error = error {
print("Error fetching logo image: \(error)")
}
guard let data = data else { return }
guard let logoImage = UIImage(data: data) else { return }
completion(companyInfo, logoImage)
}
task.resume()
} catch {
print("Error decoding JSON: \(error)")
completion(nil, nil)
}
}
task.resume()
}
}
extension NetworkServices: WebSocketDelegate {
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(_):
self.isConnected = true
DispatchQueue.main.async {
// post notification that socket is now connected
let name = Notification.Name(rawValue: isConnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
print("DEBUG: Notification \"isConnected\" posted")
}
case .disconnected(let reason, let code):
print("DEBUG: Got disconnected reason = \(reason) code = \(code)")
self.isConnected = false
case .cancelled:
print("DEBUG: cancelled.")
// socket = WebSocket(request: request)
// socket.delegate = self
// startMonitoring()
case .reconnectSuggested(let suggestReconnect):
// print("DEBUG: suggestReconnect = \(suggestReconnect)")
break
case .viabilityChanged(let viabilityChanged):
// print("DEBUG: viabilityChanged = \(viabilityChanged)")
break
case .error(let error):
print("DEBUG: error: \(String(describing: error?.localizedDescription))")
case .text(let socketString):
// print("DEBUG: .text available")
parseJSONSocketData(socketString)
default:
break
}
}
}

How to add to an NSSet using Core Data in Swift 5

So I'm practicing a little more with core data after finishing a course. So I am still a little new to it. So I Have 3 entities named Pokemon, Type & Ability. So a Pokemon can have many types like Fire,Water,Flying and so on. Type can also have multiple Pokemon that are Fire,Water,Flying and so on. Same goes for the Ability, so I made a many-to-many relationship. Here is how it looks like.
I am parsing some JSON form an api and trying to save it into core data. Now here is where I am having a bit of trouble. This is how my code looks and it just basically parse the JSON.
struct Service {
static let shared = Service()
func downloadPokemonsFromServer(completion: #escaping ()->()) {
let urlString = "https://pokeapi.co/api/v2/pokemon?limit=9"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to fetch pokemon", err)
}
guard let data = data else { return }
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let pokemonJSON = try decoder.decode(PokemonsJSON.self, from: data)
pokemonJSON.pokemons.forEach { (JSONPokemon) in
let pokemon = Pokemon(context: privateContext)
pokemon.name = JSONPokemon.name
pokemon.url = JSONPokemon.detailUrl
//Would want to set pokemon types here but
//When i call fetchMoreDetails(pokemon:,urlString:,completion:)
//The pokemon is always nil inside fetchMoreDetails
}
try privateContext.save()
try privateContext.parent?.save()
completion()
} catch let err {
print("Unable to decode PokemonJSON. Error: ",err)
completion()
}
}.resume()
}
func fetchMoreDetails(pokemon: Pokemon, urlString: String, completion: #escaping ()->()) {
guard let url = URL(string: urlString) else { return }
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = CoreDataManager.shared.persistentContainer.viewContext
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let err = error {
print("Unable to get more details for pokemon", err)
}
guard let data = data else { return }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let pokemonDetailJSON = try decoder.decode(PokemonDetailJSON.self, from: data)
pokemonDetailJSON.types.forEach { (nestedType) in
let type = Type(context: privateContext)
type.name = nestedType.type.name
//How do I add type to pokemon.types this does work
//pokemon.types?.adding(type)
}
try privateContext.save()
try privateContext.parent?.save()
completion()
} catch let err {
print("Unable to decode pokemon more details", err)
completion()
}
}.resume()
}
}
I am able to parse everything fine and all but I just can't seem to add a new type to pokemons.types. I have look on stack overflow but most of the solutions seem to be in Objective C.
This is how my ViewController looks like and I am also using a NSFetchResultController.
class PokemonTableVC: UITableViewController {
lazy var pokemonController: NSFetchedResultsController<Pokemon> = {
let context = CoreDataManager.shared.persistentContainer.viewContext
let request: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
let nameSort = NSSortDescriptor(key: "name", ascending: true)
request.sortDescriptors = [nameSort]
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
return controller
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
tableView.refreshControl = refreshControl
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Delete", style: .done, target: self, action: #selector(handleDelete))
try? pokemonController.performFetch()
}
#objc func handleDelete() {
print("Deleting")
let context = CoreDataManager.shared.persistentContainer.viewContext
guard let pokemons = pokemonController.fetchedObjects else { return }
pokemons.forEach { (pokemon) in
context.delete(pokemon)
}
do {
try context.save()
} catch let err {
print("Unable to save data", err)
}
}
#objc func handleRefresh() {
print("DDDDD")
Service.shared.downloadPokemonsFromServer {
self.pokemonController.fetchedObjects?.forEach({ (pokemon) in
print(pokemon.name)
Service.shared.fetchMoreDetails(pokemon: pokemon, urlString: pokemon.url ?? "") {
print(pokemon.abilities?.count)
}
})
}
tableView.refreshControl?.endRefreshing()
}
}
I can provide my other structs if needed. But basically I am trying to add a type to pokemon.types would also like to add fetchMoreDetails when I fetch pokemons where I put the comment at. Would
really appreciate any feedback.
When you add a relationship to an entity Xcode creates methods for getting and setting values for that relationship using a pre-defined naming standard. So you should have some methods in your Pokemon class for setting Type instances (and code completion should be able to help here):
addToTypes(value:) // single object
addToTypes(values:) //set of objects
So in your code it should be
pokemon.addToTypes(value: type)
You also have the same methods on Type for the opposite direction

Update tableview after NSBatchDeleteRequest with animation

I was wondering is there a way to update a table view with an animation, when a NSBatchRequest is executed? This code does the job, but there is no animation it just removes everything really quick.
#objc func resetButtonPressed() {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest())
let context = CoreDataManager.shared.persistentContainer.viewContext
do {
try context.execute(deleteRequest)
try context.save()
try companyController.performFetch()
tableView.reloadData()
} catch let err {
print("Unable to perform company batch delete request", err)
}
}
I am trying to get a similar animation when you use an array. The code looks something like this.
#objc func resetButtonPressed() {
// This Basically does the batch delete request
CoreDataManager.shared.deleteAllCompanies()
var indexsToRemove = [IndexPath]()
for (index, _) in companies.enumerated() {
let indexPath = IndexPath(row: index, section: 0)
indexsToRemove.append(indexPath)
}
companies.removeAll()
tableView.deleteRows(at: indexsToRemove, with: .left)
}
I tried doing this but no luck. My app just keep crashing.
#objc func resetButtonPressed() {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: Company.fetchRequest())
let context = CoreDataManager.shared.persistentContainer.viewContext
do {
var indexsToRemove = [IndexPath]()
for (_,company) in companyController.fetchedObjects!.enumerated() {
guard let indexPath = companyController.indexPath(forObject: company) else { return }
indexsToRemove.append(indexPath)
}
try context.execute(deleteRequest)
try context.save()
//Doesn't allow me to use this because its only a get
companyController.fetchedObjects?.removeAll()
//This keeps crashing my app.
tableView.deleteRows(at: indexsToRemove, with: .fade)
} catch let err {
print("Unable to perform company batch delete request", err)
}
}
Also my companyController have multiple sections. It look something like this.
lazy var companyController: NSFetchedResultsController<Company> = {
let context = CoreDataManager.shared.persistentContainer.viewContext
let request: NSFetchRequest<Company> = Company.fetchRequest()
let nameSort = NSSortDescriptor(key: "name", ascending: true)
let dateSort = NSSortDescriptor(key: "date", ascending: true)
request.sortDescriptors = [nameSort,dateSort]
//Company has a type property ex: Electronics, Gas, Groceries ...
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "type", cacheName: nil)
controller.delegate = self
do {
try controller.performFetch()
} catch let err {
print("Unable to fetch company in controller", err)
}
return controller
}()

flatMap Not returning onCompleted

I have created below function with chaining of multiple observables however whatever I do it does not seem to call completed ? it only return the following:
(facebookSignInAndFetchData()) -> subscribed
(facebookSignInAndFetchData()) -> Event next(())
even though when I debug the observables individually they all return completed
here is my chaining function
func facebookSignInAndFetchData() {
observerFacebook.flatMap { (provider: FacebookProvider) in
return provider.login()
}.flatMap { token in
return self.loginViewModel.rx_authenticate(token: token)
}.flatMap {
return self.loginViewModel.fetchProfileData()
}.debug().subscribe(onError: { error in
//Guard unknown ErrorType
guard let err = error as? AuthError else {
//Unknown error message
self.alertHelper.presentAlert(L10n.unknown)
return
}
//error message handling
switch err {
case .notLoggedIn:
print("not logged in")
break
default:
self.alertHelper.presentAlert(err.description)
}
}, onCompleted: {
self.goToInitialController()
}).addDisposableTo(self.disposeBag)
}
rx_authenticate
func rx_authenticate(token: String) -> Observable<Void> {
return Observable.create({ observer in
let credentials = SyncCredentials.facebook(token: token)
SyncUser.logIn(with: credentials, server: URL(string: Globals.serverURL)!, onCompletion: { user, error in
//Error while authenticating
guard error == nil else {
print("error while authenticating: \(error!)")
observer.onError(AuthError.unknown)
return
}
//Error while parsing user
guard let responseUser = user else {
print("error while authenticating: \(error!)")
observer.onError(AuthError.unknown)
return
}
//Authenticated
setDefaultRealmConfiguration(with: responseUser)
//next
observer.onNext()
//completed
observer.onCompleted()
})
return Disposables.create()
})
}
fetchProfileData
func fetchProfileData() -> Observable<Void> {
return Observable.create({ observer in
//Fetch facebookData
let params = ["fields" : "name, picture.width(480)"]
let graphRequest = GraphRequest(graphPath: "me", parameters: params)
graphRequest.start {
(urlResponse, requestResult) in
switch requestResult {
case .failed(_):
//Network error
observer.onError(AuthError.noConnection)
break
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
guard let identity = SyncUser.current?.identity else {
//User not logged in
observer.onError(AuthError.noUserIdentity)
return
}
//Name
let name = responseDictionary["name"] as! String
//Image dictionary
let pictureDic = responseDictionary["picture"] as! [String: Any]
let dataDic = pictureDic["data"] as! [String: Any]
let imageHeight = dataDic["height"] as! Int
let imageWidth = dataDic["width"] as! Int
let url = dataDic["url"] as! String
//Create Person object
let loggedUser = Person()
loggedUser.id = identity
loggedUser.name = name
//Create photo object
let photo = Photo()
photo.height = imageHeight
photo.width = imageWidth
photo.url = url
//Append photo object to person object
loggedUser.profileImage = photo
//Save userData
let realm = try! Realm()
try! realm.write {
realm.add(loggedUser, update: true)
}
//next
observer.onNext()
//completed
observer.onCompleted()
} else {
//Could not retrieve responseData
observer.onError(AuthError.noResponse)
}
}
}
return Disposables.create()
})
}
observerFacebook
//FacebookProvider
private lazy var observerFacebook: Observable<FacebookProvider>! = {
self.facebookButton.rx.tap.map {
return FacebookProvider(parentController: self)
}
}()
The chain starts with calling observerFacebook, which returns an observable that will emit values everytime facebookButton is tapped.
This observable will only complete when facebookButton gets released, most probably when the view controller holding it is removed from screen.
The rest of the chain will map or flatMap, but never force completion as another tap will trigger the whole chain again.
The easy way to solve this would be to add a call to take(1) on facebookButton.rx.tap, so that the function would be defined like so:
private lazy var observerFacebook: Observable<FacebookProvider>! = {
self.facebookButton.rx.tap
.take(1)
.map {
return FacebookProvider(parentController: self)
}
}()
Now, observerFacebook will complete after the first tap and you should see a call to onCompleted.
Note that you'll need to resubscribe to the chain on errors if you want to perform it again when another tap comes in.

Slow JSONSerialization Parsing Swift 3.0

I'm building an application able to return books info (Title,Description,Author ...) using Google Books Api.
The problem is that after detecting ISBN (International Standard Book Number)from the previous view it takes time to show details on my current view.
After 1-3 seconds I get parsing result:
I'm using this method to get data using google books api:
func getBookInfo(isbn: String) {
guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=isbn:\(isbn)") else {
print("***********************************")
print("the url is not valid")
return
}
URLSession.shared.dataTask(with: url, completionHandler: {data, response, error -> Void in
guard error == nil else {
print(response)
print(error!.localizedDescription)
return
}
guard let data = data else {
print("no error but no data")
print("***********************************")
print(response)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data, options: []) else {
print("the JSON is not valid")
return
}
if let arrayOfLa = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.language") as? [String] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
print("opopopop")
print (arrayOfLa)
}
}
}
///finish language
if let arrayOfTitles = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.title") as? [String] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.title = "\(arrayOfTitles[0])"
}
}
}
if let arrayOfDesc = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.description") as? [String] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.bookDescription.text = "\(arrayOfDesc[0])"
}
}
}
//start here
if let arrayOfPictures = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.imageLinks.thumbnail") as? [String] {
print ("\(arrayOfPictures[0])")
self.bookPicture = "\(arrayOfPictures[0])"
if let checkedUrl = URL(string: "\(arrayOfPictures[0])") {
self.imageBook.contentMode = .scaleAspectFit
self.downloadImage(url: checkedUrl)
}
}
//end here
//start author here
if let arrayOfAuthors = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.authors") as? [[String]] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.authorText.text = "\((arrayOfAuthors[0])[0])"
}
}
}
// finish author here
//start categories here
if let arrayOfCategories = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.categories") as? [[String]] {
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.categorieText.text = "\((arrayOfCategories[0])[0])" }
}
}
// finish caetegories here
}).resume()
}
and I'm calling it on the ViewWillAppaer
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getBookInfo(isbn: isbnCode)
}
Any suggestions ?