I'm a beginner in iOS development and I am trying to make a simple app which Passing text from text file to UITextView in "Swift 3". I have looked up many tutorials on YouTube, stack and other websites, but all of them seem to either give me a lot of errors or are too hard for me too understand (as I am too unexperienced).
There are three buttons, and three text files, and one text view.
When user clicks on the first button, file1 will be loaded into the text field.
Some of my attempts, it's give me error on txt_view.text = txt1.txt
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txt_view: UITextView!
#IBAction func btn_txt1(_ sender: Any) {
txt_view.text = txt1.txt
}
#IBAction func btn_txt2(_ sender: Any) {
txt_view.text = txt2.txt
}
#IBAction func btn_txt3(_ sender: Any) {
txt_view.text = txt3.txt
}
override func viewDidLoad(){
super.viewDidLoad()
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
}
}
This is probably the simplest way of doing it.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
#IBAction func readFile1(_ sender: Any) {
self.textView.text = load(file: "file1")
}
#IBAction func readFile2(_ sender: Any) {
self.textView.text = load(file: "file2")
}
#IBAction func readFile3(_ sender: Any) {
self.textView.text = load(file: "file3")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.textView.text = load(file: "file1")
}
func load(file name:String) -> String {
if let path = Bundle.main.path(forResource: name, ofType: "txt") {
if let contents = try? String(contentsOfFile: path) {
return contents
} else {
print("Error! - This file doesn't contain any text.")
}
} else {
print("Error! - This file doesn't exist.")
}
return ""
}
}
Following is the way to load the text file in the text view.
Swift 4.x
func loadFileContent(_ fileName: String) {
if let path = Bundle.main.path(forResource: fileName,
ofType: "txt") {
do {
let text = try String(contentsOfFile: path, encoding: .ascii)
openSourceTextView.text = text
} catch let error {
debugPrint(" Error - \(error.localizedDescription)")
}
}
NOTE:
If skip the encoding or using the encoding: .utf8 you will get the following errors -
The file “fileName.txt” couldn’t be opened because the text
encoding of its contents can’t be determined.
OR
The file “fileName.txt” couldn’t be opened using text encoding
Unicode (UTF-8).
so used encoding: .ascii while loading content of the text file.
Use this helper function:
func updateTextView(with fileName: String) {
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let path = dir.appendingPathComponent(fileName)
do {
txt_view.text = try String(contentsOf: path, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
}
You can apply it to actions like so:
#IBAction func btn_txt1(_ sender: Any) {
updateTextView(with: txt1.txt)
}
You can't read data from text file by this way.
To do it, you have can do something like this :
let file = "txt1.txt" //this is the file. we will write to and read from it
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let path = dir.appendingPathComponent(file)
catch {/* error handling here */}
//reading
do {
let text_view.txt = try String(contentsOf: path, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
if you want to simplify the syntax a bit the following is a function that would accomplish the goal as well:
private func loadTextWithFileName(_ filename: String) -> String? {
guard let path = Bundle.main.path(forResource: filename, ofType: "txt"),
let contents = try? String(contentsOfFile: path) else { return nil }
return contents
}
Related
I am trying to read Export chat Zip file but share extension loading a WhatsApp Zip attachment is not working.
I am using this code -:
override func viewDidLoad() {
super.viewDidLoad()
getURL()
}
private func getURL() {
let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem
let itemProvider = extensionItem.attachments?.first
let zip_type = String(UTType.zip.identifier)
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 }
OperationQueue.main.addOperation {
print("url\(url)")
self.path = url as URL
do {
let unzipDirectory = try Zip.quickUnzipFile(self.path)
print("unzipDirectory\(unzipDirectory)")
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(unzipDirectory.lastPathComponent)
print("fileURL\(fileURL)")
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
print(text2)
}
catch {/* error handling here */}
}
}
catch {
print("Something went wrong")
}
}
})
} else {
print("error")
}
}
override func isContentValid() -> Bool {
print("Hiii")
// Do validation of contentText and/or NSExtensionContext attachments here
return true
}
override func didSelectPost() {
print("hello")
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 []
}
in console error is:
[core] SLComposeServiceViewController got attachment coarseType 0
[core] SLComposeServiceViewController made no attachment for itemProvider conforming to public.file-url
Can anyone help please?
how can I get the source code from an URL on the logs?
This code is returning an error message on the logs instead of the HTML data.
Can you please help me and let me know what can I include/change?
Thank you!
import UIKit
class ViewController: UIViewController {
#IBOutlet var textField: UITextField!
#IBOutlet var display: UILabel!
#IBAction func send(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = URL (string: "https://www.weather-forecast.com/locations/London/forecasts/latest")!
let request = NSMutableURLRequest(url:url)
let task = URLSession.shared.dataTask (with:request as URLRequest) {
data, response, error in
if error != nil {
print (error!)
} else {
if let unrappedData = data {
let dataString = NSString(data: unrappedData, encoding: String.Encoding.utf8.rawValue)
print (dataString!)
}
}
}
task.resume()
}
}
We can get the html code from the URL like below,
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .background).async {
// Background Thread
let myURLString = "https://www.weather-forecast.com/locations/London/forecasts/latest"
guard let myURL = URL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
let myHTMLString = try String(contentsOf: myURL, encoding: .ascii)
print("HTML : \(myHTMLString)")
} catch let error {
print("Error: \(error)")
}
DispatchQueue.main.async {
// Run UI Updates or call completion block
}
}
}
}
I am saving datas on on the json file on the first VC , load the data as well and display it when switching tab. When I kill the app or re run the app again, add new datas to the JSON file, only those new datas are on the JSON file, previous datas are gone(deleted without deleting them manually) and can not be load. How do I save the file so that next time I run the program it will just append to the previous data ?
class ViewController: UIViewController {
var game : Game?
var weekLeague : [[Game]]? = []
override func viewDidLoad() {
super.viewDidLoad()
creation()
}
#IBAction func endWLButton(_ sender: UIButton) {
if games != nil {
weekLeague?.append(games!)
}
save()
}
func save(){
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
print(fileUrl)
let json = try? JSONEncoder().encode(weekLeague)
do {
try json?.write(to: fileUrl)
print(json!)
print(weekLeague)
print("JSON data was written to teh file successfully!")
}catch{
print(error)
}
}
func ShouldSendGame(game: Game) {
self.game = game
games?.append(game)
}
func creation(){
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("ArrayOfArray.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
// creating a .json file in the Documents folder
if !fileManager.fileExists(atPath: jsonFilePath!.path, isDirectory: &isDirectory) {
let created = fileManager.createFile(atPath: jsonFilePath!.path, contents: nil, attributes: nil)
if created {
print("File created ")
} else {
print("Couldn't create file for some reason")
}
} else {
print("File already exists")
}
}
}
class AllLeagueController : UITableViewController {
var arrayOfArrayGamesCopy : [[Game]] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.global().async {
self.loadData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
func loadData() {
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentsDirectoryUrl.appendingPathComponent("ArrayOfArray.json")
do{
let data = try Data(contentsOf:fileUrl)
let decoder = JSONDecoder()
let jsonData = try decoder.decode([[Game]].self, from: data)
arrayOfArrayGamesCopy = jsonData
print("Succes")
print(jsonData.count)
} catch{
print(error)
}
}
}
You need to load data here before save ... Also you need to have separate class for saving and loading data .. dot do that in controller .. its against Single Responsibility Principle ...Your load function should return array of [Game] and save data should return success or failure
#IBAction func endWLButton(_ sender: UIButton) {
//load and set data in games from file ...then append current data and save to file
if games != nil {
weekLeague?.append(games!)
}
save()
}
I have file which is shared between my app and extensions:
Write to file from extension:
func writeToFile()
{
let file = "file.txt"
let text = "data" //just a text
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!
let fileURL = dir.appendingPathComponent(file)
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */}
}
Read from the app:
func readFromFile()
{
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!
let fileURL = dir.appendingPathComponent(file)
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
NSLog("Dan: \(text2)")
}
catch {/* error handling here */}
}
My question is how can I observe changes to this file. In case the extension writes to it and changes data so the app will get notification change and read the file.
Here is a simple demo of approach based on usage NSFileCoordinator/NSFilePresenter pattern.
Tested with Xcode 11.4 / iOS 13.4
Application part. Here is a ViewController plays a file presenter role, for simplicity (if one controller can manage many files, then it is better to create explicit presenters per-file)
class ViewController: UIViewController, NSFilePresenter {
var presentedItemURL: URL?
var presentedItemOperationQueue: OperationQueue = OperationQueue.main
#IBOutlet weak var userNameField: UILabel!
func presentedItemDidChange() { // posted on changed existed file only
readFromFile()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// register for presentedItemDidChange work
NSFileCoordinator.addFilePresenter(self)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// unregister - required !!
NSFileCoordinator.removeFilePresenter(self)
}
override func viewDidLoad() {
super.viewDidLoad()
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
presentedItemURL = dir.appendingPathComponent(file)
readFromFile() // read previously stored data
}
private func readFromFile()
{
let coordinator = NSFileCoordinator(filePresenter: self)
coordinator.coordinate(readingItemAt: presentedItemURL!, options: [], error: nil) { url in
if let text2 = try? String(contentsOf: url, encoding: .utf8) {
userNameField.text = text2 // demo label in view for test
} else {
userNameField.text = "<no text>"
//just initial creation of file needed to observe following changes
coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
do {
try "".write(to: url, atomically: false, encoding: .utf8)
}
catch { print("writing failed") }
}
}
}
}
}
Extension part (simple Today extension for demo having one button)
class TodayViewController: UIViewController, NCWidgetProviding, NSFilePresenter {
var presentedItemURL: URL?
var presentedItemOperationQueue: OperationQueue = OperationQueue.main
override func viewDidLoad() {
super.viewDidLoad()
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
presentedItemURL = dir.appendingPathComponent(file)
}
#IBAction func post(_ sender: Any) { // action on button in extension
writeToFile()
}
func writeToFile()
{
let text = "new data" //just a text
let coordinator = NSFileCoordinator(filePresenter: self)
coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
do {
try text.write(to: url, atomically: false, encoding: .utf8)
}
catch { print("writing failed") }
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
completionHandler(NCUpdateResult.newData)
}
}
In a document based app I have created, I have a View called “TestDraw” where I can drag and drop UILables from the collectionView. On the “TestDraw”, free-drawing is also allowed...
I have also set BarItems for saving the position where the user put the UILabel and the information on it so that that information can later be decoded into the card object...
However, every time I open a JSON file that I think have been saved successfully, it turns out to be blank...
I have tried to print the jsonString of the saved file into the console which proves to be correct and hence shows the information is saved successfully.
Here is the code in the NumberCardsSetDocument:
class NumberCardsSetDocument: UIDocument {
var numberCardsSet: NumberCardsSet?
override func contents(forType typeName: String) throws -> Any {
// Encode your document with an instance of NSData or NSFileWrapper
return numberCardsSet?.json ?? Data()
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
if let json = contents as? Data{
numberCardsSet = NumberCardsSet(json: json)
}else {
print("Error trying to load NumberCards. Content is not JSON data")
}
}
}
Here is the code in the NumberCardsSet:
struct NumberCardsSet: Codable{
var numberCards = [CardInfo]()
struct CardInfo: Codable{
let x: Int
let y: Int
let text: String
let size: Int
}
init?(json:Data){
if let newValue = try? JSONDecoder().decode(NumberCardsSet.self, from: json){
self = newValue
}else{
return nil
}
}
var json: Data? {
return try? JSONEncoder().encode(self)
}
init (numberCards:[CardInfo]){
self.numberCards = numberCards
}
}
And this is what I am trying to do to load the saved data:
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
allowsPickingMultipleItems = false
allowsDocumentCreation = true
template = try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("UntitledHMS.json")
if template != nil {
allowsDocumentCreation = FileManager.default.createFile(atPath: template!.path, contents: Data() )
}
}
var template:URL?
// MARK: UIDocumentBrowserViewControllerDelegate
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: #escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
importHandler(template,.copy)
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
guard let sourceURL = documentURLs.first else { return }
presentDocument(at: sourceURL)
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, didImportDocumentAt sourceURL: URL, toDestinationURL destinationURL: URL) {
presentDocument(at: destinationURL)
}
func documentBrowser(_ controller: UIDocumentBrowserViewController, failedToImportDocumentAt documentURL: URL, error: Error?) {
}
// MARK: Document Presentation
func presentDocument(at documentURL: URL) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let documentVC = storyBoard.instantiateViewController(withIdentifier: "DocumentMVC")
if let trailMakingTestViewController = documentVC.contents as? DocumentViewController{
trailMakingTestViewController.document = NumberCardsSetDocument(fileURL: documentURL)
}
present(documentVC,animated: true)
}
}
I think the problem probably lies in the process of decoding the information. Hoping someone can give me some clue. This is the first time I tried to build a complicated app on my own.
I think you could narrow this down further. You have this line
return try? JSONEncoder().encode(self)
And you handle nil returns in your code, which is okay.
But if you suspect that your encoding may be failing, one way to get better information is to use a proper do - catch block around the try rather than using the try?. If you start by just printing the error to the console it might help. In my experience, these messages are helpful in pointing out why something cannot be encoded (or decoded).