Download PDF from API - swift

The API I am using has 3 parameters year month and salaryType.
When I enter those parameters to my URL
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)")
URL becomes
"https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=2020&ay=2&maasturu=1"
When I enter this URL with Auth and Token it returns a pdf file. How should I modify my getPayroll so that I can download pdf after the request?
func getPayroll(year: Int, month: Int, salaryType: Int, completion: #escaping (String) -> ()) {
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Basic \(authToken)", forHTTPHeaderField: "Authorization")
request.addValue(workingToken, forHTTPHeaderField: "token")
let task = URLSession.shared.downloadTask(with: request) { (localURL, urlResponse, error) in
if let localURL = localURL {
if let string = try? String(contentsOf: localURL) {
completion(string)
}
}
}
task.resume()
}
}
So when I tap on a cell I want to show an alert, download and then open pdf
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch cellType(rawValue: selected.title) {
case .salary:
selectedPayment = (payment?[indexPath.row])!
RtukNetworkManager.shared.getPayroll(year: selectedPayment.year, month: selectedPayment.month, salaryType: selectedPayment.paymentType) { filePath in
DispatchQueue.main.async {
self.displayDownloadAlert(filePath)
}
}
func downloadFile(path: String) {
let donwloadManager = FileDownloadManager()
donwloadManager.downloadFile(path: path, viewController: self)
}
When I tap on a cell getting an error as an alert
Error
The document can not be shown

this will save pdf file to url and also check if already downloaded
func getPayroll(year: Int, month: Int, salaryType: Int, completion: #escaping ((URL?) -> ())) {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let parameters: [String: Int] = ["yıl": year, "ay": month, "maasturru": salaryType]
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)") {
let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)
if FileManager().fileExists(atPath: destinationUrl.path) {
print("File already exists [\(destinationUrl.path)]")
completion(destinationUrl)
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Basic \(authToken)", forHTTPHeaderField: "Authorization")
request.addValue(workingToken, forHTTPHeaderField: "token")
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if error == nil {
if let response = response as? HTTPURLResponse {
if response.statusCode == 200 {
if let data = data {
if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic)
{
completion(destinationUrl)
}
else
{
completion(destinationUrl)
}
}
}
}
} else {
print(error?.localizedDescription)
completion(nil)
}
}
task.resume()
}
}
after that call your to download
RtukNetworkManager.shared.getPayroll(year: selectedPayment.year, month: selectedPayment.month, salaryType: selectedPayment.paymentType) { filePath in
if let url = filePath {
self.displayDownloadAlert(url.lastPathComponent)
showPdf(url:url)
}
}
to show pdf
func showPdf(url:URL) {
let pdfView = PDFView(frame: CGRect(x:100,y:100,width:200,height:200))
let pdfDocument = PDFDocument(url: url))
pdfView.autoresizesSubviews = true
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin]
pdfView.displayDirection = .vertical
pdfView.autoScales = true
pdfView.displayMode = .singlePage
pdfView.displaysPageBreaks = true
pdfView.document = pdfDocument
pdfView.maxScaleFactor = 4.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.usePageViewController(true, withViewOptions: [:])
view.addSubview(pdfView)
}
also import PDFKit
PDFView use for displaying pdf

Related

add headers in api Swift / xcode

I have to use an api link and the website said that I have to add my key in the header
but I don t know how to add it
I tried like this but is not working :(
can someone please help?
struct PollenRequest {
let resourceUrl: URL
let API_KEY = "123wrsgsdfhseraq24eewfesd"
init(location: String){
let resourceString = "https://api.ambeedata.com/latest/pollen/by-place?place=\(location)"
guard let resourceUrl = URL(string: resourceString) else { fatalError() }
let request = NSMutableURLRequest(url: NSURL(string: resourceString)! as URL)
request.addValue("123wrsgsdfhseraq24eewfesd",forHTTPHeaderField: "x-api-key")
self.resourceUrl = resourceUrl
}
func getPollen (completion: #escaping(Result<[PollenData], PollenError>) -> Void){
let dataTask = URLSession.shared.dataTask(with: resourceUrl){data, _, _ in
guard let jsonData = data else {
completion(.failure(.noData))
return
}
do{
let decoder = JSONDecoder()
let pollenResponse = try decoder.decode(PollenResponse.self, from: jsonData)
let pollenDetails = pollenResponse.data
completion(.success(pollenDetails))
}catch{
completion(.failure(.notProcessedData))
}
}
dataTask.resume()
}
}
Rather than saving the URL you have to save the URLRequest
And don't use NS.. types if there is a native Swift equivalent
struct PollenRequest {
let urlRequest: URLRequest
let API_KEY = "123wrsgsdfhseraq24eewfesd"
init(location: String) {
let resourceString = "https://api.ambeedata.com/latest/pollen/by-place?place=\(location)"
guard let resourceUrl = URL(string: resourceString) else { fatalError() }
var request = URLRequest(url: resourceUrl)
request.addValue(API_KEY, forHTTPHeaderField: "x-api-key")
request.addValue("application/json", forHTTPHeaderField: "Content-type")
self.urlRequest = request
}
func getPollen (completion: #escaping(Result<[PollenData], PollenError>) -> Void){
let dataTask = URLSession.shared.dataTask(with: urlRequest) {data, _, _ in
guard let jsonData = data else {
completion(.failure(.noData))
return
}
do{
let decoder = JSONDecoder()
let pollenResponse = try decoder.decode(PollenResponse.self, from: jsonData)
let pollenDetails = pollenResponse.data
completion(.success(pollenDetails))
}catch{
completion(.failure(.notProcessedData))
}
}
dataTask.resume()
}
}
Side note:
In case of an error returning .notProcessedData is meaningless. You should return the real DecodingError

Why I can't use my request adapter interceptor with Alamofire request?

I have such interceptor:
struct EnvInterceptor:RequestInterceptor,RequestRetrier {
let preferences = UserDefaults.standard
func adapt(_ urlRequest: URLRequest, for session: URLSession, completion: #escaping (Result<URLRequest, Error>) -> Void) {
var adaptedRequest = urlRequest
adaptedRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard urlRequest.url?.absoluteString.hasPrefix("/login") == true || urlRequest.url?.absoluteString.hasPrefix("/refresh") == true else {
return completion(.success(urlRequest))
}
if self.preferences.integer(forKey: "expires_in") - Int(Date().timeIntervalSince1970) - self.preferences.integer(forKey: "time_delta")<=60 {
refreshTokens()
}
adaptedRequest.addValue("Bearer " + self.preferences.string(forKey: "access_token")!, forHTTPHeaderField: "Autorization")
print("request")
completion(.success(adaptedRequest))
}
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: #escaping (RetryResult) -> Void) {
}
func refreshTokens(){
print("refresh tokens")
let parameters = ["token": self.preferences.string(forKey: "refresh_token") ?? ""]
var request = URLRequest(url: URL(string: Pathes.init().refreshTokens)!)
request.httpMethod = HTTPMethod.post.rawValue
request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []);
AF.request(request).responseJSON() { response in
switch response.result {
case .success:
guard let data = response.data else {return}
do{
let resp = try JSONDecoder().decode(RefreshToken.self, from: data)
let dfmatter = DateFormatter()
dfmatter.dateFormat="EEE, dd MMM yyyy HH:mm:ss Z"
let date = dfmatter.date(from: response.response?.allHeaderFields["Date"] as! String)
let dateStamp:TimeInterval = date!.timeIntervalSince1970
DispatchQueue.main.async {
self.preferences.set(Int(dateStamp)-Int(Date().timeIntervalSince1970), forKey: "time_delta")
self.preferences.setValue(resp.expires_in, forKey: "expires_in")
self.preferences.setValue(resp.access_token, forKey: "access_token")
self.preferences.setValue(resp.refresh_token, forKey: "refresh_token")
}
}catch{
print(error.localizedDescription)
}
case .failure(let error):
print(error.responseCode as Any)
}
}
}
}
and here I'm trying to use it:
var request = URLRequest(Pathes.init().userInfo! as URL)
request.httpMethod = "GET"
let session = Alamofire.Session(
configuration: URLSessionConfiguration.default,
interceptor: Interceptor(adapters: [EnvInterceptor()]))
session.request(request).responseDecodable(of:PersonalInfo.self){(response) in
print(response.response?.statusCode)
}
and the problem is that I can't see response. I think that I did smth wrong during interceptor building, maybe someone will see where the problem is?
Do you mean to use hasSuffix in this guard clause? Because no url string can have prefixes like "/login".
guard urlRequest.url?.absoluteString.hasPrefix("/login") == true || urlRequest.url?.absoluteString.hasPrefix("/refresh") == true else {
return completion(.success(urlRequest))
}

URLSession.shared.dataTask block not running

I'm trying to make a request to an API. Without the completion handler, the request proceeds without issue. However, now that I've added in a completion handler, the URLSession.shared.dataTask block doesn't appear to run at all; the second print statement never prints. How should I adjust my code to fix this issue?
func makeRequestToApi(word: String, completionHandler: #escaping ([String]) -> Void) {
var array = [String]()
let appId = ""
let appKey = ""
let language = "en-us"
let unwrappedURL = URL(string: "https://od-api.oxforddictionaries.com:443/api/v2/entries/\(language)/\(word)")!
var request = URLRequest(url: unwrappedURL)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
print("THIS STATEMENT PRINTS...")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
print("AND THIS STATEMENT")
if let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
do {
let root = try JSONDecoder().decode(Root.self, from: data)
let results = root.results
for result in results {
for lexical in result.lexicalEntries {
for entry in lexical.entries {
for sense in entry.senses {
for example in sense.examples {
array.append(example.text)
}
}
}
}
}
} catch {
print(error)
}
completionHandler(array)
}
}
dataTask.resume()
}
This is the function that I used on another view controller that returned data for the same words:
func makeRequestToApi() {
let appId = ""
let appKey = ""
let language = "en-us"
let strictMatch = "false"
var word = search
word = word.lowercased()
let oxDicURL = URL(string: "https://od-api.oxforddictionaries.com:443/api/v2/entries/\(language)/\(word)?strictMatch=\(strictMatch)")
if let unwrappedURL = oxDicURL {
var request = URLRequest(url: unwrappedURL)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
do {
let root = try JSONDecoder().decode(Root.self, from: data)
let results = root.results
for result in results {
for lexical in result.lexicalEntries {
for entry in lexical.entries {
for sense in entry.senses {
for example in sense.examples {
print(example.text)
self.array.append(example.text)
}
}
}
}
}
self.search = word
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "goToDetail", sender: nil)
}
} catch {
print(error)
}
}
}
dataTask.resume()
}
}

How to put image to NSCache in Swift?

I make some code using swift 4 to load image from URL, but every time I add images to server, it took a lot of time to load it in colection view or table view. I want to try store it in NScache but i dont understand to do it. can anyone help me, I'm new in swift :(
#objc func loadPosts() {
let url = URL(string: "http://someURL/Url.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "phomepost=\(homepost)"
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.collectionView?.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.collectionView?.reloadData()
//print(posts.count)
} catch {
print(error)
}
}else{
print(error)
}
})
}.resume()
}
You can use something like this:
private let cache = NSCache<NSString, NSData>()
.....
func downloadImage(url: String, handler: #escaping(Data?, Error?) -> Void){
let cacheID = NSString(string: url)
if let cachedData = cache.object(forKey: cacheID) {
handler((cachedData as Data), nil)
}else{
if let url = URL(string: url) {
let session = URLSession(configuration: urlSessionConfig)
var request = URLRequest(url: url)
request.cachePolicy = .returnCacheDataElseLoad
request.httpMethod = "get"
session.dataTask(with: request) { (data, response, error) in
if let _data = data {
self.cache.setObject(_data as NSData, forKey: cacheID)
handler(_data, nil)
}else{
handler(nil, error)
}
}.resume()
} else {
// NetworkError is a custom error
handler(nil, NetworkError.invalidURL)
}
}
}
}
This will add a small animation while loading using image set.
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageFromUrl(urlString: String) {
let loader1 = UIImage(named: "loaderImage1.png")
let loader2 = UIImage(named: "loaderImage2.png")
let loader3 = UIImage(named: "loaderImage3.png")
let imageArray = [loader1, loader2, loader3]
let animatedImage = UIImage.animatedImage(with: imageArray as! [UIImage], duration: 1.7)
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
self.image = imageFromCache
return
} else {
self.image = animatedImage
Alamofire.request(urlString, method: .get).response { (responseData) in
if let data = responseData.data {
DispatchQueue.main.async {
if let imageToCache = UIImage(data: data){
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
self.image = imageToCache
}
}
}
} //alamofire
}
}
}

How to return songs from Apple Music API?

I need add a new function that returns songs from Apple Music API. The code below returns nil however, it works for Album ID. I modified the code from APIClient.swift. See source for details.
It returns song(id:completion:) JSON Decode Failed.
Source: https://github.com/shingohry/AppleMusicSearch
SongModel.swift
func song(id: String, completion: #escaping (Resource?) -> Swift.Void) {
let completionOnMain: (Resource?) -> Void = { resource in
DispatchQueue.main.async {
completion(resource)
}
}
guard let url = URL(string: "https://api.music.apple.com/v1/catalog/\(APIClient.countryCode)/songs/\(id)") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Bearer \(APIClient.developerToken)",
forHTTPHeaderField: "Authorization")
data(with: request) { data, error -> Void in
guard error == nil else {
print(#function, "URL Session Task Failed", error!)
completionOnMain(nil)
return
}
guard let jsonData = try? JSONSerialization.jsonObject(with: data!),
let dictionary = jsonData as? [String: Any],
let dataArray = dictionary["data"] as? [[String: Any]],
let songDictionary = dataArray.first,
let songData = try? JSONSerialization.data(withJSONObject: albumDictionary),
let album = try? JSONDecoder().decode(Resource.self, from: songData) else {
print(#function, "JSON Decode Failed");
completionOnMain(nil)
return
}
completionOnMain(song)
}
}