i currently developpe an app which request data from a webservice, and one the datas are retrieve, i call a method to create some "swype" card like tinder.
The problem is, i do not success to call my method in the viewDidload, this is my code :
if(success == 1) {
NSLog("Recherche OK");
//Call the swype method
}
This is my method :
func fetchData(int: Int, completion: (()->())?) {
let newCard = Model()
//ttp://thecatapi.com/api/images/get?format=src&type=png
if let url = NSURL(string: "http://test.com/api/images/get?format=src&type=png") {
if let data = NSData(contentsOfURL: url) {
newCard.image = UIImage(data: data)
newCard.content = "test"
newCard.desc = "test"
self.data.append(newCard)
NSLog("fetch new data")
}
Have you got an idea ?
Please read more about getting NSData from a network call. Following is from a Apple Documentation
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 NSSession class. See
URL Loading System Programming Guide for details.
Also I noticed that the url you are showing redirects, so maybe that is the problem. Instead of trying to load it in a UIImage, you can create a UIWebview and show the image there.
You can find Swift related help regarding that in answer here
Related
I am trying to follow the WWDC20 session "Keep your complications up to date", WWDC20-10049. In trying to follow with the code, I have gotten stuck on the schedule function:
func schedule(_ first: Bool) {
if backgroundTask == nil { //Issue is here
if let url = self.currentWeatherURLForLocation(delegate.currentLocationCoordinate)
{
let bgTask = backgroundURLSession.downloadTask(with: url)
bgTask.earliestBeginDate = Date().addingTimeInterval(first ? 60 : 15*60)
bgTask.countOfBytesClientExpectsToSend = 200
bgTask.countOfBytesClientExpectsToReceive = 1024
bgTask.resume()
backgroundTask = bgTask //Issue is here
}
}
}
}
The issue I am having, since the session only provided snippets, is where the var backgroundTask is defined. This function is declared in the data model, in this case WeatherDataProvider. It is obviously a backgroundURLSession.downloadTask, but it is not identified as self so it should be local to the function, but it is not shown in the snippet. I would expect it to be either sent in as a parameter to the function, or declared in the data model itself. If it is declared there, how is it declared as, according to the talk, it has access to prior background requests as the snippet is checkin to see if there is a background request in the queue so the function doesn't schedule another one. I have searched for the last couple of days, but can find nothing that implements this that I can use to understand where Apple is coming from in this session.
I have checked out this answer, as well as this overview of the session, but I haven't figured this out.
Thanks.
I am trying to get file name from PHAsset reference provided by UIImagePickerController,How do I retrieve the image file name when choose from library and take photo with camera?
I am using Xcode 11.4 and Swift 5.
Given, you already have the PHAsset object, PHAssetResource is what you need to work retrieve the filename.
let assetResources = PHAssetResource.assetResources(for: asset)
This will return you an array of PHAssetResource instances, among which one of them is the current image.
Inspecting the PHAssetResource instances on the debugger, you can see it contains multiple files. This may include adjustment data, represented by .plist, and one or more photos.
To get the file that is currently accessible, what you need to look for is a key isCurrent. Apple hasn't exposed this key as a variable for PHAssetResource, however, they don't take any effort hiding the value. And I assume, querying this value doesn't result in an app rejection.
You can query this value directly, or can do it more cleanly by extending PHAssetResource.
extension PHAssetResource {
var isCurrent: Bool? {
self.value(forKey: "isCurrent") as? Bool
}
}
Now, you can ask the [PHAssetResource], for the first item that isCurrent as well as a photo or video.
let resourceTypes: [PHAssetResourceType] = [.photo, .video, .audio, .fullSizePhoto]
let currentAssetResource = assetResources.first(
where: { $0.isCurrent == true && resourceTypes.contains($0.type)
})
And, finally for the filename.
let fileName = currentAssetResource?.originalFilename
Note
PhotoKit expects the developer to work with the abstract asset resource, and not to deal with the underlying files or data. The usage of isCurrent seems to be allowed. However, the value can be altered or removed with an iOS update. Do it only if you have a strong use-case for extracting the filename.
There are two approaches:
You can retrieve the URL of the temporary file that UIImagePickerController creates for you using info[.imageURL]. If you only need, for example, the extension of the asset, this is sufficient.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
// url of temporary file provided by image picker
if let url = info[.imageURL] as? URL {
print("temp URL", url) // file:///private/var/mobile/Containers/Data/Application/.../tmp/FAC0D82B-85A9-4C7A-B69C-58BB53F74CDC.jpeg
}
dismiss(animated: true)
}
But the filename, itself, bears no relationship to the original asset.
You can retrieve the original asset URL from the Photos framework. We used to be able to use PHImageManager, call requestImageDataForAsset, and then use "PHImageFileURLKey" in the resulting info dictionary, but that doesn’t work anymore.
But as suggested elsewhere, you can asset.requestContentEditingInput, and that includes the URL of the asset:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
// url of the original asset
if let asset = info[.phAsset] as? PHAsset {
asset.requestContentEditingInput(with: nil) { input, info in
if let fileURL = input?.fullSizeImageURL {
print("original URL", fileURL) // file:///var/mobile/Media/DCIM/100APPLE/IMG_0135.HEIC
}
}
}
dismiss(animated: true)
}
If you do this, you obviously need to request permissions for the photos library, notably:
if PHPhotoLibrary.authorizationStatus() == .notDetermined {
PHPhotoLibrary.requestAuthorization { granted in
print(granted)
}
}
And you need to supply a NSPhotoLibraryUsageDescription usage string in your Info.plist.
Personally, it feels wrong to request content editing input when you don’t really need to edit, but AFAIK, that’s the only way to get the original asset name nowadays. (There’s got to be a better way: If you browse the PHAsset in the debugger, the asset name is actually buried in there, but not exposed ... maybe they did that to avoid confusion with the HEIC extension.)
Bottom line, it just depends upon what you need the name for. If you just need the path for the temporary file, then use .imageURL. If you need information about the original asset, use .phAsset and the Photos/PhotoKit framework.
Recently, I attempted to write my own Telegram Bot API. However, the project has seem to have hit a brick wall with URLSession (formerly NSURLSession) issues.
The call structure is as follows:
getMe() -> getData() -> NSURLSession
Ideally, I would like to have the data returned from NSURLSession passed back to getMe() for the application to process. However, this has not proven possible with the methods I have tried.
Below is the code I have been using. synthesiseURL() generates the URL that the app should open the session to in order to perform the action on the Telegram Bot API. A template of the URL generated by synthesiseURL() is https://api.telegram.org/bot\(token)/\(tgMethod).
// NSURLSession getData: gets data from Telegram Bot API
func getData(tgMethod: String, arguments: [String] = [String](), caller: String = #function) {
let url = synthesiseURL(tgMethod: "getMe"), request = NSMutableURLRequest(url: url)
var receivedData = String()
let session = URLSession.shared.dataTask(with: request as URLRequest) { data, response, err in
if err != nil {print(err!.localizedDescription); return}
DispatchQueue.main.async {
receivedData = String(data: data!, encoding: String.Encoding.nonLossyASCII)!
print(receivedData)
}
}
session.resume()
}
I have been trying to get getData to pass receivedData, which contains the Bot API's response, back to the function getMe.
func getMe() -> String {
HTTPInterface(botToken: token).get(tgMethod: "getMe")
return [???] // here's where the data from getData() should come
}
I have tried completion handlers, callbacks, asynchronous calls to the main thread etc, but none seem to be working as expected (getMe() returns an empty string).
Why is this so, and can it be fixed?
The fundamental issue is that your getMe() function is declared as having an immediate String return type, but it depends on a delayed / asynchronous call to get that string. The timeline looks something like this:
getMe() is called by some client code
getMe() kicks of the method that launches a URLSession to get the data
getMe() moves to the next line of execution and returns a string (still empty at this point). The getMe() function has now returned and the client code execution has continued forward with the empty String result
The URLSession completes with data, but execution has already moved on so the data doesn't get used anywhere
The easiest fix is to make your getMe function not have a return type, but to also call back to a closure parameter when the URLSession data comes back, something like:
func getMe(callback:String->()) {
//getData and pass a closure that executes the callback closure with the String data that comes back
}
The less easy fix is to use a technique like dispatch semaphores to prevent getMe() from returning a result until the URLSession data comes back. But this sort of approach is likely to stall your main thread and is unlikely to be the right choice.
func forwardGeocoding(address: String) {
CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
if error != nil {
print(error)
return
}
if placemarks?.count > 0 {
let placemark = placemarks?[0]
let location = placemark?.location
let coordinate = location?.coordinate
print("\nlat: \(coordinate!.latitude), long: \(coordinate!.longitude)")
if placemark?.areasOfInterest?.count > 0 {
let areaOfInterest = placemark!.areasOfInterest![0]
print(areaOfInterest)
} else {
print("No area of interest found.")
}
}
})
var INITIAL_DESTINATION = forwardGeocoding(initialDestination)
var DESIRED_DESTINATION = forwardGeocoding(desiredDestination)
var location = CLLocationCoordinate2DMake(<#T##CLLocationDegrees#>, <#T##CLLocationDegrees#>)
Hello, I am trying to make a mapping app, and am having trouble with this part. What I want to do is be able to separate the INITIAL_DESTINATION latitude and longitudes. I have to do this to create a CLLocationCoordinate2DMake. What I have been trying to do is just use INITIAL_DESTINATION.latitude and INITIAL_DESTINATION.longitude, but I am continuingly facing the same error which is "Value of tuple type "()" has no member "latitude". This is also strange because it does not give that error for INITIAL_DESTINATION.longitude.
Any help or suggestions are greatly appreciated, and thank you for reading and taking the time to respond.
Your function returns nothing, and does nothing with the value returned in the asynchronous completion handler. You need to take the asynchronous result and use it in some fashion.
Try this: Put prints at the end of the function, and inside the completion handler, then run the code. What you'll see is that the function is done before the completion handler runs, because the code inside the block does not run until the remote web site returns an answer across the network. At that time Alamofire hands the result to your code in the completion block.
You'll also need to be aware that there are multiple queues in iOS, and UI changes can only be done on the main queue. The completion block does not run on the main queue, however, so likely to use the information returned from the network you'll need to use the dispatch_async function to call a function in your program and have it execute on the main queue.
I watched with a great attention the WWDC 2015 sessions about Advanced NSOperations and I played a little bit with the example code.
The provided abstraction are really great, but there is something I may did not really good understand.
I would like to pass result data between two consequent Operation subclasses without using a MOC.
Imagine I have a APIQueryOperation which has a NSData? property and a second operation ParseJSONOperation consuming this property. How do I provide this NSData? intance to the second operation ?
I tried something like this :
queryOperation = APIQueryOperation(request: registerAPICall)
parseOperation = ParseJSONOperation(data: queryOperation.responseData)
parseOperation.addDependency(queryOperation)
But when I enter in the execute method of the ParseJSONOperation the instance in not the same as the same as in the initialiser.
What did I do wrong ?
Your issue is that you are constructing your ParseJSONOperation with a nil value. Since you have two operations that rely on this NSData object I would suggest you write a wrapper object to house this data.
To try and be aligned with the WWDC talk lets call this object the APIResultContext:
class APIResultContext {
var data: NSData?
}
now we can pass this object into both the APIQueryOperation and the ParseJSONOperation so that we have a valid object that can store the data transferred from the API.
This would make the constructors for the query:
let context = APIResultContext()
APIQueryOperation(request: registerAPICall, context: context)
ParseJSONOperation(context: context)
Inside your ParseJSONOperation you should be able to access the data assuming the query completes after it sets the data.
Thread Safety
As #CouchDeveloper pointed out, data is not strictly speaking thread safe. For this trivial example since the two operations are dependent we can safely write and read knowing that these accesses wont take place at the same time. However, to round the solution up and make the context thread safe we can add a simple NSLock
class APIResultContext {
var data: NSData? {
set {
lock.lock()
_data = newValue
lock.unlock()
}
get {
lock.lock()
var result = _data
lock.unlock()
return result
}
}
private var _data: NSData?
private let lock = NSLock()
}