PerformSegueWithIdentifier not working Swift - ios

I have a tableView on my page and when I press a specific row, my app is supposed to show another page using segue. However when i click on the appropriate row it freezes, and then when I click a different row the segue finally shows up, which is odd. The code used to work for me but for some reason stopped, and I can't identify the issue because the code is identical (at least as far as I can see). Here is a code snippet :
func restClient(client: DBRestClient!, loadedFile destPath: String!, contentType: String!, metadata: DBMetadata!){
let title = "This format is incorrect"
let message = "You can only download file that is in .txt format"
let okText = "OK"
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let okayButton = UIAlertAction(title: okText, style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(okayButton)
if contentType.rangeOfString("text") != nil{
print("this is text")
self.performSegueWithIdentifier("segue", sender: nil)
}
else{
print("this is an error")
presentViewController(alert, animated: true, completion: nil)
return;
}
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
print("The file \(metadata.filename) was downloaded. Content type: \(contentType). The path to it is : \(documentsDirectoryPath)" )
progressBar.hidden = true
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let localFilePath = documentsURL.URLByAppendingPathComponent("Documents")
let checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath(localFilePath.path!))
{
print("FILE AVAILABLE");
}
else
{
print("FILE NOT AVAILABLE");
}
return
}
didDeselectRowAtIndexPath code :
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath){
let selectedFile: DBMetadata = dropboxMetadata.contents[indexPath.row] as! DBMetadata
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let localFilePath = (documentsDirectoryPath as NSString).stringByAppendingPathComponent(selectedFile.filename)
print("The file to download is at: \(selectedFile.path)")
print("The documents directory path to download to is : \(documentsDirectoryPath)")
print("The local file should be: \(localFilePath)")
showProgressBar()
dbRestClient.loadFile(selectedFile.path, intoPath: localFilePath as String)
}
The picture below illustrates when I click on the file.txt row. As you can see it just stays grey and nothing happens, but after that, if I click on another file, say enumrec.pdf, it will show the appropriate page. Would be happy if anyone could point out what i am doing wrong here.

You should use didSelectRowAtIndexPath instead of didDeselectRowAtIndexPath. The latest is often propose first when using autocompletion, it's easy to make the mistake.

It looks like you are performing your seque after your data call which is on a different thread (so your app can carry on when your data call is running). When you change UI you have to run it on the main thread otherwise you can have issues like this. Just wrap your performSegue code in this:
dispatch_async(dispatch_get_main_queue(),{
self.performSegueWithIdentifier("segue", sender: nil)
});
You can read about threads/backgrounds tasks etc here:
https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Related

Add variable to URL in swift

I am currently developing an app in Xcode with Swift. The general premise of the app is that when the app first loads, the user will be asked to enter a 'mobile linking code' this will essentially be added onto the end of the url as a variable which will serve as their login to the site, the rest of the authentication is done server side. Then when the user logs into the app each time after that, this variable will be auto applied to the URL so that essentially they are always auto logged in.
I have my code setup for the app and the UIAlertController loads with a text field, I am struggling on how to find out how to append the 'mobile linking code' (which the user will type into the text field) to the end of the URL on first load and then also how to append this to the URL that loads each time after that.
The code I have is as follows
At the top of my WebViewController.swift
var webviewurl = "https://mywebsite.co.uk/loginarea.php?link=" (I need to append the mobile link code to the end of that url)
Further down in my code I have my first run dialog, in which I have added a UIAlertController. This will be ran on the very first time opening the app only in which the user will input their 'mobile link code', upon clicking Submit, the webview should be redirected to the url with the data in the text field appended to the end.
if activatefirstrundialog == "true" {
if !user.bool(forKey: "firstrun")
{
user.set("1", forKey: "firstrun")
user.synchronize()
webView.stopLoading()
let ac = UIAlertController(title: "Enter Mobile Link Code", message: "Enter the mobile link code found in your Client Area", preferredStyle: .alert)
ac.addTextField()
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
let answer = ac.textFields![0]
// do something interesting with "answer" here
let url = URL(string: "https://mywebsite.co.uk/loginarea.php?link=")!
self.webView.load(URLRequest(url: url + answer))
}
ac.addAction(submitAction)
present(ac, animated: true)
}
}
}
I would be eternally grateful if someone could help me with this.
TIA
To use the mobileLink between multiple app sessions, you need to save it somewhere after it is entered for the first time by the user.
Let's say we save it in UserDefaults. You can then fetch its value accordingly like so,
if !user.bool(forKey: "firstrun") {
//your code...
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
if let answer = ac.textFields.first?.text {
UserDefaults.standard.set(answer, forKey: "mobileLink")
let url = URL(string: "https://mywebsite.co.uk/loginarea.php?link=\(answer)")!
self.webView.load(URLRequest(url: url))
}
}
//your code...
} else if let mobileLink = UserDefaults.standard.string(forKey: "mobileLink") {
let url = URL(string: "https://mywebsite.co.uk/loginarea.php?link=\(mobileLink)")!
self.webView.load(URLRequest(url: url))
}
Did you mean something like this?
let submitAction = UIAlertAction(title: "Submit", style: .default) { [unowned ac] _ in
let answer = ac.textFields![0]
// do something interesting with "answer" here
if let answerText = answer.text, answerText.count > 0 {
if let percentageEncodedString = "https://mywebsite.co.uk/loginarea.php?link=\(answerText)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let url = URL(string:percentageEncodedString) {
self.webView.load(URLRequest(url: url))
} else {
//Handle error here, url is not valid
}
} else {
//Handle error here, url cannot be encoded
}
} else {
//Handle error here, text field's text is nil or text is empty
}
}
You should store this mobile linking code somewhere safe. you can use keychain to store it.
To answer your question, you can concatenate the string and form a URL Like this:
let url = URL(string: "https://mywebsite.co.uk/loginarea.php?link=\(answer)")!
Here answer is a variable.

How to make iMessage to open my app with a custom document type

EDIT: As requested added info.pslist and code.
I have a custom Document Type and I have registered the UTIs and a new MIME type. I basically followed the steps by this tutorial. I am using a Codable object that it is no more than a JSON file with a custom extension and a custom icon. Honestly it looks pretty cool to me. The app I am doing is is a Grocery list app that makes a lot of sense being able to share it by a note or iMessage.
Like the finalised app in the tutorial I followed it opens in mail and even in notes!!! but iMessage does not recognise the extension and shows a folder icon and does not open it.
My question is how can I tell iMessage that this file is meant to be opened by my App. Do I need an iMessage extension? I am pretty new to iOS. info.pslist:
And now code:
func exportToUrl() -> URL? {
let contents = try? JSONEncoder().encode(shoppingList)
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let saveUrl = path.appendingPathComponent("/list.grabgrocerieslist")
try? contents?.write(to: saveUrl, options: .atomic)
return saveUrl
}
#IBAction func sharedTapped(_ sender: UIBarButtonItem) {
guard let url = exportToUrl() else {
return
}
let activityController = UIActivityViewController(activityItems: ["Shopping List", url], applicationActivities: nil)
activityController.excludedActivityTypes = [.assignToContact, .saveToCameraRoll, .postToFacebook ]
activityController.popoverPresentationController?.barButtonItem = sender
self.present(activityController, animated: true, completion: nil)
}
Many Thanks,

FileManager changes are not working for reordering and has ErrorDomain Code=17 "File exists"

I'm completely new to iOS developing and Swift.
I want to implement edit mode for my table view which shows a list of files like the image below:
Everything works fine in edit mode, and my codes works fine in deletion, but when I change the place of my files by dragging them, they will come back to their first place each time. The code for changing the file manager wold not run and it has this error:
NSUnderlyingError=0x1c0249c90 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}`
This line will not call:
do {
try FileManager.default.moveItem(atPath: (documentURL?.path)!, toPath: (documentURL_Dest?.path)!)
}
In here, this is my moveRowAt function in the view controller:
var documents = [PDFDocument]()
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = self.documents[sourceIndexPath.row]
let document = movedObject
let documentURL = document.documentURL
let document_Dest = documents[destinationIndexPath.row]
let documentURL_Dest = document_Dest.documentURL
documents.remove(at: sourceIndexPath.row)
documents.insert(movedObject, at: destinationIndexPath.row)
//this do wouldn't call and catch is calling each time
do {
try FileManager.default.moveItem(atPath: (documentURL?.path)!, toPath: (documentURL_Dest?.path)!)
} catch let error {
NSLog("Error in copying Data.plist: \(error)") // see the above quoted error message from here
}
refreshData()
}
and here is my refreshData() function:
private func refreshData() {
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let contents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
documents = contents.flatMap { PDFDocument(url: $0) }
tableView.reloadData()
}
#objc func documentDirectoryDidChange(_ notification: Notification) {
refreshData()
}
I put this line code for enabling edit mode in viewDidLoad:
self.navigationItem.rightBarButtonItem = self.editButtonItem
The error is pretty much self-explanatory, FileManager doesn't let you move a file to a folder where a file with the same name already exists.
You shouldn't load the files again if you want to change the files' order in your list, since that will reset the original order.
If you are not planning to show more than one folder in your file or you don't want to add a function to move files to another location, you should remove this whole chunk of your code:
do {
try FileManager.default.moveItem(atPath: (documentURL?.path)!, toPath: (documentURL_Dest?.path)!)
} catch let error {
NSLog("Error in copying Data.plist: \(error)") // see the above quoted error message from here
}
refreshData()
Otherwise, you should handle the error accordingly (for e.g. show an alert to let the user that a file already exists with the same name in that directory).

IBAction func on button not triggering

I have 3 buttons in a VC, all hooked up to IBAction functions. Two of them work fine but the Submit button simply simply won't trigger.
I have made sure User Interaction is enabled. I have also tried adding sender: AnyObject as a parameter and re-hooking up the function to the button but still no luck. I have also cleaned the project. I am very baffled as to what is going on.
Here is how the VC looks:
Hooking the buttons up:
Accessibility of button:
Here is the code for each IBAction func:
#IBAction func captureImage(){
self.saveVideoVar = false
let imageFromSource = UIImagePickerController()
imageFromSource.delegate = self
imageFromSource.allowsEditing = false
//if there is a camera
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
imageFromSource.sourceType = UIImagePickerControllerSourceType.Camera
self.presentViewController(imageFromSource, animated: true){}
}
else{
let title = "Error"
let message = "Could not load camera"
let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func openImageLibrary(){
self.saveVideoVar = false
let imageFromSource = UIImagePickerController()
imageFromSource.delegate = self
imageFromSource.allowsEditing = false
imageFromSource.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
//presents (loads) the library
self.presentViewController(imageFromSource, animated: true){}
}
//code to submit image and video to amazon S3
#IBAction func submitToS3(){
print("x")
if let img : UIImage = imageView.image! as UIImage{
let path = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("image.png")
let imageData: NSData = UIImagePNGRepresentation(img)!
imageData.writeToFile(path as String, atomically: true)
// once the image is saved we can use the path to create a local fileurl
let url:NSURL = NSURL(fileURLWithPath: path as String)
nwyt.uploadS3(url)
}
}
Screenshot of control clicking the Submit button:
OH MY GOD! I feel stupid. There was a duplicate screen I had forgotten to delete that looked exactly the same but wasn't the one that was being displayed. I'm going to delete this in an hour. Below was the problem:
Check by setting background colors to the buttons so that you can understand whether any view is over the button or not .
I can see that there is an extra ":" in "submitToS3:", meaning that the function submitToS3 should have an argument, which is not your case.
In order to solve this, just remove the submitToS3 link, and then drag and drop from the Submit button to the yellow icon above in the controller, and link it to the "submitToS3" (you should see it there). When looking back at the Received Actions view, you should not see the ":"
Try this:
//code to submit image and video to amazon S3
#IBAction func submitToS3(sender:UIButton){
print("x")
if let img : UIImage = imageView.image! as UIImage{
let path = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("image.png")
let imageData: NSData = UIImagePNGRepresentation(img)!
imageData.writeToFile(path as String, atomically: true)
// once the image is saved we can use the path to create a local fileurl
let url:NSURL = NSURL(fileURLWithPath: path as String)
nwyt.uploadS3(url)
}
}
Seems callback argument was missing. Should work. Finger crossed!!
Please check the enabled property in property inspector!
Check button name!
Create a new button and new method. Try and hook. I had faced this kinda problem. Could be xCode issue if you are using xCode 7.

UIDocumentInteractionController always fails

I'm trying to use the UIDocumentInteractionController. I've checked everything I can think off and everything appears to have all the data that is needed.
When calling presentOpenInMenuFromRect(inView:, animated:) the Bool that is returned is always false. My understanding is that its because there aren't any apps that support the UTI. I've tried using a flat out PNG and still nothing. Whats interesting is why it wouldn't work on my iPhone which has plenty of apps that support loading images via the UIDocumentInteractionController, but here is the kicker. This code was working fine before I upgraded the project to Swift 1.2.
Am I overlooking something now? I've checked the docs and couldn't find anything that I was missing.
Note: I have tested this on a device and in the simulator and both return the same.
let image = UIImage(named: "screenshot")
if let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) {
if paths.count > 0 {
if let dirPath = paths[0] as? String {
let path = dirPath.stringByAppendingPathComponent("screenshot.ig") // igo
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), { () -> Void in
if let image = image {
NSFileManager.defaultManager().removeItemAtPath(path, error: nil)
if UIImagePNGRepresentation(image).writeToFile(path, atomically: true) {
}
}
})
if let url = NSURL(fileURLWithPath: path) {
let documentController = UIDocumentInteractionController(URL: url)
documentController.UTI = "com.instagram.photo" // com.instagram.exclusivegram
documentController.annotation = ["InstagramCaption": ""]
if !documentController.presentOpenInMenuFromRect(sender.bounds, inView: self.view, animated: true) {
println("Can't do it")
}
}
}
}
}
I decided to go with the presentOptionsMenuFromRect(:, inView:, animated:) which does what I am trying to accomplish. No idea why presentOpenInMenuFromRect(: inView:, animated:) decided to break down, but I did mange to get it all working.
The problem is you are writing your file asynchronously on a background queue, but do not wait before opening your interaction controller. So when it attempts to open your file, it cannot find it because it hasn't been written just yet. You need to let the write operation succeed, then attempt to open the interaction controller.
Change your code like so:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), { () -> Void in
if let image = image {
NSFileManager.defaultManager().removeItemAtPath(path, error: nil)
if UIImagePNGRepresentation(image).writeToFile(path, atomically: true) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if let url = NSURL(fileURLWithPath: path) {
let documentController = UIDocumentInteractionController(URL: url)
documentController.UTI = "com.instagram.photo" // com.instagram.exclusivegram
documentController.annotation = ["InstagramCaption": ""]
if !documentController.presentOpenInMenuFromRect(sender.bounds, inView: self.view, animated: true) {
println("Can't do it")
}
}
}
}
}
})
I had the same problem on an iPad which was pretty much right out of the box. I couldn't open txt or png files (the two tests I ran). presentOpenInMenuFromRect always returned NO.
I found that the accepted answer of using presentOptionsMenuFromRect did launch that window, but it wasn't precisely what I wanted. (It could be what some of you will want though).
It turned out that my device simply didn't have an app associated with either of these types by default! (How ridiculous is that?) I assumed they were so common that would never be an issue.
So, I randomly installed the first free file manager app that I found in the app store (USB Disk Free was what I picked). Then, my device gave me the option to open either of those types with that app.

Resources