Can't write string to file inside of NSSavePanel "ok" function - swift

For some reason, I can only write a string to a file outside of NSSavePanel's ok function. I need to write it as soon as the user says "OK, I do want to save that".
Here is my code:
//An IBAction that connects to the "Save" menu item.
#IBAction func SaveButton(_ sender: Any) {
os_log("Save button pressed.")
//Declares savePanel to be equal to NSSavePanel opens the save panel in a seperate window.
let savePanel = NSSavePanel()
savePanel.runModal()
let textEntryController = EntryViewController()
//Sets a placeholder of the text we're going to write.
func ok(_ sender: Any?){
let entryPath = savePanel.url
let entryFieldContents = textEntryController.entryTextField!;
let entryText = (entryFieldContents.textStorage as NSAttributedString?)?.string
let entryContent = entryText
do {
try entryContent?.write(to: entryPath!, atomically: true, encoding: String.Encoding.utf8)
} catch {
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
}
}

Instead of using runModal() you should use the more modern closure syntax:
let savePanel = NSSavePanel()
savePanel.begin { (response) in
if response == .OK {
// write it here
}
}

Related

Download single Object of Firestore and save it into an struct/class object

I am coding since January 2019 and this is my first post here.
I am using Swift and Firestore. In my App is a tableView where I display events loaded out of a single Document with an array of events inside as [String: [String:Any]]. If the user wants to get more infos about an event he taps on it. In the background the TableViewController will open a new "DetailEventViewController" with a segue and give it the value of the eventID in the tapped cell.
When the user is on the DetailViewController Screen the app will download a new Document with the EventID as key for the document.
I wanna save this Data out of Firestore in a Struct called Event. For this example just with Event(eventName: String).
When I get all the data I can print it directly out but I can't save it in a variable and print it out later. I really don't know why. If I print the struct INSIDE the brackets where I get the data its working but if I save it into a variable and try to use this variable it says its nil.
So how can I fetch data out of Firestore and save in just a Single ValueObject (var currentEvent = Event? -> currentEvent = Event.event(for: data as [String:Any]) )
I search in google, firebaseDoc and stackoverflow but didn't find anything about it so I tried to save all the singe infos of the data inside a singe value.
// Struct
struct Event {
var eventName: String!
static func event(for eventData: [String:Any]) -> Event? {
guard let _eventName = eventData["eventName"] as? String
else {
print("error")
return nil
}
return Event(eventName: _eventName)
}
// TableView VC this should work
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowEventDetailSegue" {
if let ShowEvent = segue.destination as? DetailEventViewController, let event = eventForSegue{
ShowEvent.currentEventId = event.eventID
}
}
}
// DetailViewController
var currentEvent = Event()
var currentEventId: String?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let _eventID = currentEventId else {
print("error in EventID")
return}
setupEvent(eventID: _eventID) /* currentEvent should be set here */
setupView(event: currentEvent) /* currentEvent has after "setupEvent" the value of nil */
}
func setupEvent(eventID: String) {
let FirestoreRef = Firestore.firestore().collection("events").document(eventID)
FirestoreRef.getDocument { (document, error) in
if let err = error {
debugPrint("Error fetching docs: \(err)")
SVProgressHUD.showError(withStatus: "Error in Download")
}else {
if let document = document, document.exists {
guard let data = document.data() else {return}
let eventData = Event.event(for: data as [String:Any])
print(eventData)
//here all infos are printed out - so I get them
self.currentEvent = eventData!
//Here is the error.. I can't save the fetched Data in my single current Event
} else {
SVProgressHUD.showError(withStatus: "Error")
}
}
}
}
func setupView(event: Event) {
self.titleLabel.text = event.eventName
}
I expect that the function setupEvents will give the currentEvent in the DetailViewController a SINGLEvalue cause its a SINGLE document not an array. So I can use this single Eventvalue for further actions. Like starting a new segue for a new ViewController and just push the Event there not

NSKeyedUnarchiver.unarchiveObject() unarchives old object

I want to save the user's filter selections on FilterViewController.
When FilterViewController is closed, NSKeyedArchiver.archiveRootObject archives the user's selections. However, NSKeyedUnarchiver.unarchiveObject opens up the initial default selections (NOT the user's selections). How to fix this?
FiltersViewController.swift
override func viewWillAppear(_ animated: Bool) {
if let filterSections = NSKeyedUnarchiver.unarchiveObject(withFile: filterViewModel.filtersFilePath) as? [FilterSection] {
// Retrieves initial default selections, NOT user's selection
filterViewModel.filterSections = filterSections
filtersTableView.reloadData()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Saves what user selects
let isSuccessful = NSKeyedArchiver.archiveRootObject(self.filterViewModel.filterSections, toFile: self.filterViewModel.filtersFilePath)
if (isSuccessful) {
print("Saved filters") // This is printed
} else {
print("Didn't Save filters")
}
}
FilterViewModel.swift
class FilterViewModel: NSObject {
// Contains all filtered sections displayed on table view
var filterSections: [FilterSection] = []
// File Path to saved Filter Sections
var filtersFilePath: String {
let manager = FileManager.default
let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
print("this is the url path in the documentDirectory \(url)")
return (url!.appendingPathComponent("FilterSelectionData").path)
}
override init() {
super.init()
filterSections = self.getFilterSections()
}
}
CompanyViewController.swift
#objc func filterButtonTapped() {
var filterViewModel: FilterViewModel
if (self.filterViewModel != nil) {
filterViewModel = self.filterViewModel! // This runs
}
else {
self.filterViewModel = FilterViewModel()
filterViewModel = self.filterViewModel!
}
let filtersVC = FiltersViewController(filterViewModel: filterViewModel)
self.navigationController?.pushViewController(filtersVC, animated: true)
}
You are using self.getFilterSections to set filterSections in FilterViewModel init. I suppose self.getFilterSections is a method that returns the default values. For me, it should not be the case, rather if you have archived some values, you should get that in this method. Although, this alone should not be the reason for the issue, but may be a reason for inducing bug. Try changing self.getFilterSections to return archived values if possible otherwise default values and check whether the bug is still there.

How to extract a zip file and get the extracted components in a Share Extension in Swift

I need to do the following-
I have another app in which i will export the users config(.txt) and contacts(.vcf) in a zip format.
In the second app i have a share extension to get the exported zip and in the share extension, i need to extract the zip file and get both the txt and vcf files and then upload them to a parse server.
I have done till opening the exported zip in the share extension. but i could not get the zip extracted.
I couldn't get the answer in internet.
Here is my ShareViewController
import UIKit
import Social
import Parse
import MobileCoreServices
import SSZipArchive
class ShareViewController: SLComposeServiceViewController {
var requird_data : NSData!
var path : URL!
override func viewDidLoad() {
super.viewDidLoad()
//Parse.setApplicationId("cGFyc2UtYXBwLXdob3N1cA==", clientKey: "")
initUI()
getURL()
textView.delegate = self
textView.keyboardType = .numberPad
}
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(true)
//
// }
func initUI()
{
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
title = "upup"
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.backgroundColor = UIColor(red:0.97, green:0.44, blue:0.12, alpha:1.00)
placeholder = "Please enter your Phone number"
}
private func getURL() {
let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem
let itemProvider = extensionItem.attachments?.first as! NSItemProvider
let zip_type = String(kUTTypeZipArchive)
if itemProvider.hasItemConformingToTypeIdentifier(zip_type) {
itemProvider.loadItem(forTypeIdentifier: zip_type, options: nil, completionHandler: { (item, error) -> Void in
guard let url = item as? NSURL else { return }
print("\(item.debugDescription)")
OperationQueue.main.addOperation {
self.path = url as URL
SSZipArchive.unzipFile(atPath: url.path!, toDestination: url.path!)
}
})
} else {
print("error")
}
}
override func isContentValid() -> Bool {
// Do validation of contentText and/or NSExtensionContext attachments here
return true
}
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
override func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
{
let length = ((textView.text)?.characters.count)! + text.characters.count - range.length
let allowedset : CharacterSet = CharacterSet(charactersIn: "0123456789+").inverted as CharacterSet
let filtered = (text.components(separatedBy: allowedset)).joined(separator: "")
return (length<17) && (text == filtered)
}
}
I use SSZipAchive to extract the file. Link : https://github.com/ZipArchive/ZipArchive
I ran the application in the Xcode 9 beta 1. I used the new Files app from simulator to share the zip.
Below is my Share Extensions Info.Plist
I am newbie to share extension so i don't know much about it. All the code above are from bits and pieces from the following tutorials and a little googling.
1.https://www.appcoda.com/ios8-share-extension-swift/
2.https://hackernoon.com/how-to-build-an-ios-share-extension-in-swift-4a2019935b2e
Please guide me.
I use swift 3.
I found out the solution. It was my mistake to give the destination file path for the extracted items to be the same as the source files path. After changing it to the app's documents directory i got it working.
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
SSZipArchive.unzipFile(atPath: url.path!, toDestination: documentsPath)

Calling a function inside a function?

I have the following function (which uses this awesome library) for a button that captures data (copies data) from a cell and then tries to call another function if the user taps imgtagAction. The first button func buttonViewLinkAction works great. I get the AlertView and I'm presented with another button imgtagAction. However, when I click on that button I get:
unrecognized selector sent to instance.
//get buttonViewLinkAction and copy to pasteboard
#IBAction func buttonViewLinkAction(sender: UIButton) {
print("buttonViewLinkAction tapped")
let face = self.faces[sender.tag]
if let imageNAME: String = String(face.name){
print(imageNAME .uppercaseString)
}
if let imageURL = NSURL(string:face.image) {
print(imageURL)
}
UIPasteboard.generalPasteboard().string = face.directLink
let alertView = SCLAlertView()
alertView.addButton("Add [imag] tags", target:self, selector:Selector("imgtagAction:"))
alertView.showSuccess((face.name), subTitle: "Direct link copied to clipboard")
func imgtagAction(Sender: AnyObject) {
print("imgtagAction tapped")
UIPasteboard.generalPasteboard().string = "[img]" + face.directLink + "[/img]"
}
}
So, when I move the imgtagAction function outside of the buttonViewLinkAction function I can't get access to the cell data.
func imgtagAction(Sender: AnyObject) {
print("imgtagAction tapped")
let face = self.faces[sender.tag]
if let imageNAME: String = String(face.name){
print(imageNAME .uppercaseString)
}
if let imageURL = NSURL(string:face.image) {
print(imageURL)
}
UIPasteboard.generalPasteboard().string = "[img]" + face.directLink + "[/img]"
}
The error I get is:
use of unresolved identifier 'sender'.
What am I doing wrong here?
You have Sender instead of sender. This is what your function should look like.
func imgtagAction(sender: AnyObject)
{
print("imgtagAction tapped")
let face = self.faces[sender.tag]
if let imageNAME: String = String(face.name)
{
print(imageNAME .uppercaseString)
}
if let imageURL = NSURL(string:face.image)
{
print(imageURL)
}
UIPasteboard.generalPasteboard().string = "[img]" + face.directLink + "[/img]"
}

issue receiving outcomes when sending text to wit.ai

I'm using the following to send text to wit.ai through a button press function:
#IBAction func searchButton(sender: AnyObject) {
searchQueryText = searchTextInput.text!
if searchQueryText != "" {
wit.interpretString(searchQueryText, customData: nil)
}
func interpretString(string: String, customData: AnyObject) {
}
this works fine as the text is sent to wit.ai. However I get no response from wit.ai back to the app. I can get the response fine if a microphone is used, just not text. I have tried calling the witDidGraspIntent function to force it to run on button press, but I can't work out what I should use in the 'outcomes' parameter. Can anybody help on this? I'm not sure if there is a different way to run the function after button press? This is the function:
func witDidGraspIntent(outcomes: [AnyObject]!, messageId: String!, customData: AnyObject!, error e: NSError!) {
if ((e) != nil) {
print("\(e.localizedDescription)")
return
}
let outcomes : NSArray = outcomes!
let firstOutcome : NSDictionary = outcomes.objectAtIndex(0) as! NSDictionary
if let intent = firstOutcome.objectForKey("intent") as? String {
searchResultsIntent = intent
}
if searchResultsIntent == "searchIntent" {
intentLabel.text = "\(searchResultsIntent)"
print(outcomes[0])
} else {
intentLabel.text = "I'm sorry, I did not understand that."
}
}
here is the documentation for wit.ai: https://wit.ai/docs/ios/4.0.0/api
any assistance is greatly appreciated!
cheers.
Wit sdk gives a sharedInstance (singleton) for users to work on, so you have initiate it like -:
Wit.sharedInstance().accessToken = "TOKEN"
Wit.sharedInstance().delegate = self
and invoke the interpretString function using the sharedInstance i.e.
Wit.sharedInstance().interpretString(text, customData: nil)