I am using SwiftUI in Xcode 11, trying to check the content of a .txt file from the internet.
The problem is that the URLSession.shared.downloadTask takes time to finish. The code to check the content is always performed before the download is finished. Can anyone help me please? Thanks very much.
Sorry, forgot to add some codes.
let url = URL(string: "https://www.myweb.com/myfile.txt”)!
var myweb = “test”
URLSession.shared.downloadTask(with: url) { localURL, response, error in
if let localURL = localURL {
do { try myweb = String(contentsOf: localURL)}
catch { print (“test”) }
}
}.resume()
if myweb != “test” { Call some function here}
I assume that you need to create ViewModel with Published property and change it flag on true statement if downloadTask has finished. Use this property inside View
Related
I am writing a Mac app in SwiftUI and would like to display a live-updated list of documents and folders with the ability to edit the files.
First, users selects any folder with Open File Dialog and then I save the URL into UserDefaults and attempt to read list of files and folders when the app launches.
Assuming I have a valid URL then I do the following:
// Open the folder referenced by URL for monitoring only.
monitoredFolderFileDescriptor = open(url.path, O_EVTONLY)
// Define a dispatch source monitoring the folder for additions, deletions, and renamings.
folderMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredFolderFileDescriptor, eventMask: .write, queue: folderMonitorQueue)
App is crashes when I call DispatchSource.makeFileSystemObjectSource with the EXC_BREAKPOINT exception.
FileManager.default.isReadableFile(atPath: url.path) returns false which tells me I don't have permissions to access this folder.
The URL path is /Users/username/Documents/Folder
I have added NSDocumentsFolderUsageDescription into the info plist.
It's not clear how can I ask for permission programmatically.
Theoretically my URL can point to any folder on File System that the user selects in the Open Dialog. It's unclear what is the best practice to request permission only when necessary. Should I parse the URL for the "Documents" or "Downloads" string?
I also have watched this WWDC video.
Thanks for reading, here's what example what I am trying to show.
Like #vadian said, this needs Secure Scoped Bookmark. If you user picks a folder from NSOpenPanel, permission dialog not necessary. This answer helped me a lot.
I create new NSOpenPanel which gives me URL?, I pass this URL to the saveAccess() function below:
let bookmarksPath = "bookmarksPath"
var bookmarks: [URL: Data] = [:]
func saveAccess(url: URL) {
do {
let data = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
bookmarks[url] = data
NSKeyedArchiver.archiveRootObject(bookmarks, toFile: bookmarksPath)
} catch {
print(error)
}
}
After you save bookmark, you can access when you app launches.
func getAccess() {
guard let bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: bookmarksPath) as? [URL: Data] else {
print("Nothing here")
return
}
guard let fileURL = bookmarks.first?.key else {
print("No bookmarks found")
return
}
guard let data = bookmarks.first?.value else {
print("No data found")
return
}
var isStale = false
do {
let newURL = try URL(resolvingBookmarkData: data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
newURL.startAccessingSecurityScopedResource()
print("Start access")
} catch {
print(error)
return
}
}
The code is very rough, but I hope it can help someone. After acquiring the newURL, you can print content of a folder.
let files = try FileManager.default.contentsOfDirectory(at: newURL, includingPropertiesForKeys: [.localizedNameKey, .creationDateKey], options: .skipsHiddenFiles)
And don't forget to call .stopAccessingSecurityScopedResource() when you done.
Using Swift 4 and Xcode 10
I am trying to make a GET request to an API and get the results in json, and my code works perfectly in my playground, but when I copy it over to my app, I get a "Program ended with exit code: 0" error.
I'd like to make this a function I can call and change up the headers, httpMethod, credentials, actionURL, etc.. so I can reuse it for different calls to this API.
This is my first shot at this, and have been searching all over.
1) This is where I borrowed most of the code for this part of the project. Error parsing JSON in swift and loop in array
2) I tried using the advice in this video to build a struct for the data. https://www.youtube.com/watch?v=WwT2EyAVLmI&t=105s
Not sure if it is something on the swift side, or the xcode side...
import Foundation
import Cocoa
// removed in project, works in playgrounds
//import PlaygroundSupport
func makeGetCall() {
// Set up the URL request
let baseURL: String = "https://ws.example.net/v1/action"
guard let url = URL(string: baseURL) else {
print("Error: cannot create URL")
return
}
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// set up auth
let token = "MYTOKEN"
let key = "MYKEY"
let loginString = String(format: "%#:%#", token, key)
let loginData = loginString.data(using: String.Encoding.utf8)?.base64EncodedString()
// make the request
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
let task = session.dataTask(with: request) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let apiResponse = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// let's just print it to prove we can access it
print(apiResponse)
// the apiResponse object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
//guard let todoTitle = todo["title"] as? String else {
// print("Could not get todo title from JSON")
// return
//}
//print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
makeGetCall()
// removed in project, works in playgrounds
//PlaygroundPage.current.needsIndefiniteExecution = true
In playgrounds I receive the json response as expected, but when I copy the code over to my project I receive an error.
Expected output example:
["facet": <__NSArrayI 0x7fb4305d1b40>(
asnNumber,
assetPriority,
assetType,
etc...
"Program ended with exit code: 0" is not an error; it just means the program came to an end. In fact, it is the opposite of an error, since it means the program came to an end in good order.
So, what kind of project template did you use? I'm guessing you used a Cocoa command-line tool. In that case, the problem is that simply you have no runloop, so we reach the last line and come to an end before any asynchronous stuff (e.g. networking) can take place.
This is exactly parallel to what you had to do in your playground. You couldn't network asynchronously without needsIndefiniteExecution; the playground needs to keep running after it has reached the last line, to give the networking a chance to happen. In the same way, you need the runloop in the command-line tool to keep going after it has reached the last line, to give the networking a chance to happen.
For how to give your command-line tool a runloop, see for example Run NSRunLoop in a Cocoa command-line program
Alternatively, don't use a command-line tool. Make an actual app. Now the app persists (until you quit it), and asynchronous networking just works.
I am working on code to open files which have been downloaded from the app, and I am using the utils.ios.openFile function. I can get the files to display on screen, however I am also getting the following warning on the console. Has anyone run into this, or have any ideas on how to resolve?
Unbalanced calls to begin/end appearance transitions for
. Cannot find
preview item for proxy: -
mobile-application.png (0)
The following is the code I am using:
var documents = fs.knownFolders.documents();
filePath = fs.path.join(documents.path, filename);
if(fs.File.exists(filePath)){
utilModule.ios.openFile(filePath)
.catch(function(error){
});
} else {
return Promise.reject(new Error("File not Found"));
}
You could set the filePath to the image, without using openFile(filePath) method. You could review the below-attached example.
let folder = fs.knownFolders.documents();
let path = fs.path.join(folder.path, "Test.png");
if(fs.File.exists(path )){
image.src=path
}
So basically I am making an event app. Everything has been going smoothly but there's just sharing the event to twitter.
I have searched the internet but all I am getting is using the native app of twitter which I don't want. I want to use the browser to tweet.
I have implemented this method for FB sharing.
Any idea would help me a lot.
let content = FBSDKShareLinkContent()
content.contentURL=NSURL(string: "http://facebook.com")
content.imageURL = NSURL(string: "http://facebook.com")
content.contentTitle = "Shou 3emlin test app "
content.contentDescription = "testing testing testing"
let shareDialog = FBSDKShareDialog()
shareDialog.fromViewController = self
shareDialog.mode=FBSDKShareDialogMode.Browser
shareDialog.shareContent = content
if !shareDialog.canShow() {
shareDialog.mode=FBSDKShareDialogMode.Native
shareDialog.shareContent = content
}
if shareDialog.canShow() {
shareDialog.show()
}
Put this in an action method of a button or in the method where you want to use the browser to tweet your text Swift 3.0:
let tweetText = "your text"
let tweetUrl = "http://stackoverflow.com/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)
Result:
Take a look at Fabric.io. This SDK allows you to compose tweets directly from your app.
let composer = TWTRComposer()
composer.setText("just setting up my Fabric")
composer.setImage(UIImage(named: "fabric"))
// Called from a UIViewController
composer.showFromViewController(self) { result in
if (result == TWTRComposerResult.Cancelled) {
print("Tweet composition cancelled")
}
else {
print("Sending tweet!")
}
}
let tweetText = "hy"
let tweetUrl = "http://rimmi/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)
#ronatory's solution worked like charm. It also opens a Twitter application if it's already installed on the user's device.
For swift 5+ use UIApplication.shared.open(url!) instead of UIApplication.shared.openURL(url!) as it's deprecated.
So i've tried looking over the documentation and cant make heads or tails of it... so i'm hoping someone here can help:
so i've created a ParseFile with an image stored in a byte[]. I've saved this parsefile and then assigned it into a parseObject. I have then saved the parseObject
profilePicFile = new ParseFile( "profilePic.png", bytes);
Task saveProfilePic = profilePicFile.SaveAsync();
yield return saveProfilePic;
user["Profile_Pic"] = profilePicFile;
Task updateUser = user.SaveAsync();
Then i've made a temporary button just to check that this works. I've assigned it a new script. Basically when I hit the button, I just want it to grab the user, and its profile_pic and tell me if there is something there.
ParseObject receivedUser = new ParseObject("ReceivedUser");
ParseQuery<ParseObject> query = ParseObject.GetQuery("User");
query.GetAsync("DwRfTQ66tA").ContinueWith(t =>
{
receivedUser = t.Result;
});
if (receivedUser.IsDataAvailable == true) {
print ("getting data");
byte[] data = receivedUser.Get<byte[]> ("Profile_Pic");
} else {
print ("no user");
}
}
Am I doing this right? Do i need to re-initialise anything? Do I need to add the other script component to this or use Getcomponent to get the user data? (I dont think so since the ParseUser object is supposed to be a static right?). Or does this script need to re-log in to grab data from Parse?
currently the error i'm getting is KeyNotFoundException.
I deff have a User Class on parse and a Profile_Pic column. I'm using the object ID as the reference. Is this correct?
Instead of
ParseObject.GetQuery("User")
you should be using:
ParseUser.Query