How Can I get Image data from specific URI in Swift? - swift

I have a function called downloadImage. I'm trying to get image data and set an imageView. But it prints "no data".
image link: http://commons.wikimedia.org/wiki/Special:FilePath/Flag%20of%20Montenegro.svg
Here is my code:
func downloadImage() {
let uri = "Special:FilePath/Flag%20of%20Montenegro.svg"
let baseURL = URL(string: "http://commons.wikimedia.org/wiki/")!
let imageURL = URL(string: uri, relativeTo: baseURL)!
if let data = try? Data(contentsOf: imageURL){
if let image = UIImage(data: data){
DispatchQueue.main.async {
self.flagImageView.image = image
}
}
else{
print("image cannot be taken")
}
}else{
print("no data")
}
}
Here is console output:
2022-09-03 21:21:56.426579+0300 CountryBook[5242:197393] nil host used in call to
allowsSpecificHTTPSCertificateForHost
2022-09-03 21:21:56.426913+0300 CountryBook[5242:197393] nil host used in call to allowsAnyHTTPSCertificateForHost:
2022-09-03 21:21:56.427580+0300 CountryBook[5242:197905] NSURLConnection finished with error - code -1002
no data
Note:
I Allowed Arbitrary Loads from info.plist by marking it as YES.

Assuming you did allow the usage of http corectly. There is more going on here:
The link you provided redirects to https://upload.wikimedia.org/wikipedia/commons/6/64/Flag_of_Montenegro.svg. Data(contentsOf: is not for this purpose. It is best suited for loading data from a bundle file url and not complex redirecting or cookies or header..... . Use a proper URLSesssion.
Even if you get your data it will not work this way. UIImage doesn´t support SVG format. See this SO question for more info
Remarks:
Just use https. It´s de facto standard. And your link to wikipedia would support it. Falling back to http should be the last resort while developing and never in production.

this is actually due to the fact that apple no longer allows http urls by default, it wants them to be https
When using a https url, you also shouldn't need to allowArbitraryLoads
From what I suspect though, wikipedia is https, so I think you can just change your url to have https like so:
https://commons.wikimedia.org/wiki/Special:FilePath/Flag%20of%20Montenegro.svg

Related

Keeping the constant part of a URL as a variable available to all view controllers

I am currently expressing JSON data by loading the information from a URL, the URL is from an API that comes in two forms:
test.example.com and example.com
Full links throughout the applications will always end differently after the forward slash:
test.example.com/example1 ... test.example.com/example2
But the beginning will always be one of the 2 forms above
I would like to the ability to easily switch between the two URL by changing it in one place using perhaps an extension that is available for all view controller.
So for example I have:
private func JSON() {
guard let url = URL(string: "https://test.example.com/example"),
let sample = value1.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "example1=\(example)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.JStruct = try JSONDecoder().decode([exampleStruct].self,from:data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
What would be the best approach for switching between the two urls?
UPDATE:
Is it wrong to just use:
struct URLVar {
static var url = "https://example1.com/example/"
}
and reference URLVar.url when needed?
What I normally do is maintain an api.plist file (a key value file store) and an environment.plist file. The ending part of the APIs will be stored in the api.plist, while the domain parts example.com will be stored in the environment.plist.
Note that this way, I can have multiple environment.plist files pointing to different places (a production server, a staging server, etc...). I can simply switch in the necessary environment.plist using a build script phase.
To read, I use the normal plist reading mechanisms provided by apple and combine the domainUrl (in environment.plist) with the required resource path (in api.plist). The following link will explore reading options for you:
https://learnappmaking.com/plist-property-list-swift-how-to/
You can also look at the following library that adds some beautiful code generation capabilities for .plist files among others:
https://github.com/SwiftGen/SwiftGen

Swift, Kingfisher Cache not working properly?

I am trying to cache images using the Cocoapod KingFisher, the code i am using does display the image from the database storage but it does no caching. I am curious as to know why?
The print always says "cache Result none". And i also notice that the images are not cached.
Code for calling the imageDownloader:
DownloadImage(imageId : nextUser.id, cardImage: secondProfilePic)
Code for downloading and caching, also for checking if cached.
func DownloadImage(imageId : String, cardImage : UIImageView){
let imagesStorageRef = Storage.storage().reference().child("profilepic/").child(imageId)
//Get URL For Cache
imagesStorageRef.downloadURL { url, error in
if let error = error {
// Handle any errors
cardImage.image = UIImage(named: "RentOutProfilePic")
print("Error")
} else {
// Get the download URL for '.jpg'
let pathURL = url
print("Sets Image")
cardImage.kf.indicatorType = .activity
cardImage.kf.setImage(with: pathURL,
options: [
.transition(.fade(0.3)),
.cacheOriginalImage
])
}
if let url = url{
let tempUrl:String = url.path
let cache = ImageCache.default
let cached = cache.imageCachedType(forKey: tempUrl)
print("cache Result \(cached)")
}
}
}
Kingfisher is using url.absoluteString as the cache key for an image by default. So in your code, url.path will always return you the result of "not cached".
You are trying to print the cache result as the same time when you set the image. At the first time, your image would be in download progress so you always get .none even you set the key correctly according to 1. In the following invocation of this method with the same id, you should get a cache result either as disk or memory.
I am not sure how did you get the conclusion of "the images are not cached". Kingfisher is doing cache based on url by default. If you have different urls every time (which is returned from your imagesStorageRef) you call the image view setting method, there would be no matching cache and downloading would happen. If this is your case, you can customize to use the imageId as the cache key instead. To do that, you need to specify another cache key. See this wiki section for more.

contentsof:url loads url content of a truncated URL

When I use contentsof:url it truncates the url before retrieving the content, resulting in different html content than the displayed in the WKWebView.
For example
contents = try String(contentsOf: https://www.amazon.com/gp/aw/d/B00BECJ4R8/ref=mp_s_a_1_1?ie=UTF8&qid=1531620716&sr=8-1-spons&pi=AC_SX236_SY340_QL65&keywords=cole+haan&psc=1)
returns the contents of this page: https://www.amazon.com/gp/aw/d/B00BECJ4R8/
Why is this happening? Is there an alternative method that allow you to read the content of the actual URL not the truncated URL?
Any advice if very much appreciated.
Thank you.
You shouldn't be using String(contentsOf:) to load a website. You should use the URL Loading System for this work then passing that object back to your webView.load(_:) method in viewDidLoad()
let urlString = "https://www.amazon.com/dp/B00BECJ4R8/?tag=stackoverflow17-20"
// URL construct may fail in case of the String not being a properly formatted URL, so unwrap it.
if let url = URL(string: urlString) {
// Create a URLRequest instance with the new url.
let request = URLRequest(url: url)
// Load the request.
webView.load(request)
}

my http request does not work

I have update my xcode and i have problem with https requests. I need to get html code. But some sites is get good, some is nil. i think problem with plist file, but presvious version in log say about Clear Http and unsecured loads. The current xcode - no. Why i have catch with some web sites? Yandex.ru - i can get html content. Google.com - i cannot(go to catch block). This problem with different sites. Part of them i can get, second part i cant. Thank you for resolve my problem.
There is my code:
if let url = URL(string: "http://google.com") {
do {
let contents = try String(contentsOf: url)
print(contents)
} catch {
print("catch")
}
} else {
print("else")
}
my plist file in attach:
Rather than catching with a print("catch") you should try to print the error that's being thrown
catch (let e) {
print(e)
}
The error I am seeing is Error Domain=NSCocoaErrorDomain Code=261 "The file couldn’t be opened using text encoding Unicode (UTF-8)." UserInfo={NSURL=http://google.com, NSStringEncoding=4}
I tried
let contents = try String(contentsOf: url, encoding: String.Encoding.utf8)
but that doesn't seem to solve the problem. Depends on how Google is serving up their pages
MOST IMPORTANTLY
This is a blocking call - you should use an async method for loading remote URLs. This method is really for loading local assets from your Bundle. Please look at NSURLRequest https://developer.apple.com/documentation/foundation/nsurlrequest and NSURLSession https://developer.apple.com/documentation/foundation/nsurlsession
I find problem - all this web sites is not UTF-8. I try different and it is work:
var serverString = NSString(data: data!, encoding: String.Encoding.windowsCP1251.rawValue)

Firebase Storage retrieval images

I'm trying to get my head around Firebase Storage.
I've seen online and tried two methods of getting my images.
What is the difference between these two? (both work).
So after I get the photoUrl from my Firebase Database:
1.
if let data = NSData(contentsOfURL: NSURL(string:photoUrl)!)
{
let myImage = UIImage(data: data)!
MyImageCache.sharedCache.setObject(myImage, forKey: self.key)
//etc
}
2.
self.storage.referenceForURL(photoUrl).dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil)
{
print(error)
}
else
{
let myImage = UIImage(data: data!)
MyImageCache.sharedCache.setObject(myImage!, forKey: self.key)
//etc
}
}
Regarding the first method, you shouldn't be using that for network calls. From the docs:
Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated. Instead, for non-file URLs, consider using the dataTaskWithURL:completionHandler: method of the NSURLSession class. See URL Session Programming Guide for details.
The second method is baked into the firebase framework and provides you with convenience methods for downloading an image, i.e. it gives you the option to specify images size. This is probably optimised for getting images and is in most scenarios the preferred method.