I'm trying to create a dictionary app on IOS. I have a text file (words_alpha.txt) in my Bundle, and I want to read all the words/lines, and place them into an arrray. String = ["word1", "word2", "word3"]. Here is my current code I got from bit.ly/39IC642
let path = Bundle.main.path(forResource: "words_alpha", ofType: "txt") // file path for file "words_alpha.txt"
let string = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
I am getting this error: Cannot use instance member 'path' within property initializer; property initializers run before 'self' is available
I am fairly new to using Swift and coding in general, please be detailed with your answers. Thank you!
If you are writing an iOS app, you can move such initialization into viewDidLoad():
var wordArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
//...
let url = Bundle.main.url(forResource: "words_alpha", withExtension: "txt")! // file URL for file "words_alpha.txt"
do {
let string = try String(contentsOf: url, encoding: .utf8)
wordArray = string.components(separatedBy: CharacterSet.newlines)
} catch {
print(error)
}
}
If your words_alpha.txt does contain multiple words per line, you may need some other way.
Related
Ok I have read so much about NSArray NSDictionary I'm lost now, what I want is to print the value 'name' from the first array item of my custom plist.
This is my plist:
and this is my code in my ViewController.swift:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let path = Bundle.main.path(forResource: "test", ofType: "plist")
let dic = NSArray(contentsOfFile: path!)
print(dic?.firstObject)
}
}
in my console I see:
Optional({
active = 0;
name = "John Doe";
})
I would think that print(dic?.firstObject["name"]) would do the trick
but I get an error: Value of type 'Any?' has no subscripts
So how do I print the values of name and active of my first array?
I know there are lots of answers on SO regarding this question, that's the reason I got so far.
but I just don't know how to fix this.
Kind regards,
Ralph
First of all please never use the NSArray/NSDictionary related API in Swift to read a property list. You are throwing away the type information.
However you can read the values with
let array = NSArray(contentsOfFile: path!) as! [[String:Any]]
for item in array {
let name = item["name"] as! String
let active = item["active"] as! Bool
print(name, active)
}
The dedicated and recommended API is PropertyListSerialization :
let url = Bundle.main.url(forResource: "test", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let array = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [[String:Any]]
A better way is the Codable protocol and PropertyListDecoder
struct User : Decodable {
let name : String
let active : Bool
}
override func viewDidLoad() {
super.viewDidLoad()
let url = Bundle.main.url(forResource: "test", withExtension: "plist")!
let data = try! Data(contentsOf: url)
let array = try! PropertyListDecoder().decode([User].self, from: data)
for item in array {
print(item.name, item.active)
}
}
The code must not crash. If it does you made a design mistake
To use subscripts you first need to cast the object returned by
dic?firstObject
to a dictionary, you can also unwrap the optional at this point.
if let item = dic?firstObject as? [String: Any] {
print(item["name")
}
I'm trying to display a .log file in a textview but can't seem to access the filepath properly. When I do it returns nil. I'm using a pod called "SwiftyBeaver" to do the logging. This is what the fileURL looks like:
file:///var/mobile/Containers/Data/Application/5C92E3E6-9E45-4869-9142-AB9E70EE4FCC/Library/Caches/swiftybeaver.log
This is the function I'm using to turn the .log into a string so I can display it in a textView
private func loadTextWithFileName(_ fileName: String) -> String? {
guard let path = Bundle.main.path(forResource: fileName, ofType: "log"),
let contents = try? String(contentsOfFile: path) else {return nil}
return contents
}
This is how I'm displaying the text to the textView
self.loggingTextView.text =
self.loadTextWithFileName(self.file.logFileURL!.absoluteString)
The method that you are using Bundle.main.path() is mainly to search for files in your Bundle.
But it seems your log file is going to be in your Cache directory.
Here is how you can look for a file in your Cache directory of your app
private func loadTextWithFileName(_ fileName: String) -> String? {
if let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(fileName)
guard let text = try? String(contentsOf: fileURL, encoding: .utf8) else {
return nil
}
return text
}
return nil
}
You can add a catch block to your try case to check what the error is, in case you don't get the log file details.
Swift is telling me my file does not exist and unfortunately I cannot find the directory myself. Am I using the URL for resource method incorrectly or do I just need to find a way of removing the optional brackets? When I refer to this object directly it works but I need to find a way of creating a path my team mates can use as well.
override func setUp()
{
super.setUp()
let bundleURL = NSBundle.mainBundle().URLForResource("meetingexample", withExtension: "ics")
let eventsFile = NSURL(fileURLWithPath: ("\(bundleURL)"))
//let eventsFile = NSURL(fileURLWithPath: docFolderURL.URLByAppendingPathComponent("/meetingexample.ics"))
content = try! String(contentsOfURL: eventsFile, encoding: NSUTF8StringEncoding)
}
Print of bundleURL:
Optional(file:/Users/GB112922/Library/Developer/CoreSimulator/Devices/EF6A2594-6B31-4E38-B46D-2F3AAAF25210/data/Containers/Bundle/Application/35E04003-072F-476E-957E-98C70B792539/CallIn.app/meetingexample.ics)`
You have two issues:
"\(bundleURL)" is wrapping the value of the NSURL in the Optional(...) extra text.
URLForResource is already giving you an NSURL. There's no need to create another NSURL from it.
Just use bundleURL. There is no need for the eventsFile variable.
override func setUp()
{
super.setUp()
let bundleURL = NSBundle.mainBundle().URLForResource("meetingexample", withExtension: "ics")
content = try! String(contentsOfURL: bundleURL, encoding: NSUTF8StringEncoding)
}
i want to put a .txt file in my Xcode Swift Iphone project.
First, i simply drag&dropped it from my desktop to the Supporting Files Folder from the project. Isn't there a Folder like on Android "assets", so i can place my files anywhere i want?
The file in my example is called README.txt which has a bunch of lines and paragraphs.
Simple enough, now I want to print the content of the README.txt file to a view.
How do i do the read function and what path should I insert, if my file is in the project /SupportFiles/README.txt?
Thanks alot!
if let path = Bundle.main.path(forResource: "README", ofType: "txt") {
do {
textView.text = try String(contentsOfFile: path, encoding: .utf8)
} catch let error {
// Handle error here
}
}
Just drop the file anywhere into the project browser and make sure it is added to the right target.
Just to expand on the answer, you can also place them in a folder and use:
+ pathForResource:ofType:inDirectory:.
I would recommend to use NSFileManager and drop your file anywhere in your project :
if let path = NSBundle.mainBundle().pathForResource(name, ofType: "txt"){
let fm = NSFileManager()
let exists = fm.fileExistsAtPath(path)
if(exists){
let c = fm.contentsAtPath(path)
let cString = NSString(data: c!, encoding: NSUTF8StringEncoding)
ret = cString as! String
}
}
Swift 4 (thanks to #Pierre-Yves Guillemet for original)
As long as the file is in your project (and has a .txt) this will work (in this example, I assume "MyFile.txt" is a file that is in my project):
static func LoadFileAsString() -> ()
{
if let path = Bundle.main.path(forResource: "MyFile", ofType: "txt")
{
let fm = FileManager()
let exists = fm.fileExists(atPath: path)
if(exists){
let content = fm.contents(atPath: path)
let contentAsString = String(data: content!, encoding: String.Encoding.utf8)
}
}
}
In Swift 3
guard let path = Bundle.main.path(forResource: "README", ofType: "txt") else {
return
}
textView.text = try? String(contentsOfFile: path, encoding: String.Encoding.utf8)
If you define path's type and put ! end of the line, you won't get any warning.
let path:String = NSBundle.mainBundle().pathForResource("README", ofType: "txt")!
textView.text = String(contentsOfFile: path,
encoding: NSUTF8StringEncoding,
error: nil)
There's a Assets.xcassets for images and files.
And here's how to read it from assets.
if let data = NSDataAsset(name: "AssetName")?.data {
textView.text = String(data: data, encoding: .utf8)
}
How do I load a text file line by line into an array with swift?
Something along the lines of:
func arrayFromContentsOfFileWithName(fileName: String) -> [String]? {
guard let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "txt") else {
return nil
}
do {
let content = try String(contentsOfFile:path, encoding: NSUTF8StringEncoding)
return content.componentsSeparatedByString("\n")
} catch _ as NSError {
return nil
}
}
This approach assumes the file in question is located in your app bundle.
With Swift 5, according to your needs, you can choose one of the 3 following ways in order to solve your problem.
#1. Using StringProtocol's components(separatedBy:) method
Foundation provides String a method called components(separatedBy:) with the following declaration:
func components(separatedBy separator: CharacterSet) -> [String]
Returns an array containing substrings from the string that have been divided by characters in the given set.
The code sample below shows how to use components(separatedBy:) with its parameter set to CharacterSet.newlines in order to load the content of a text file line by line into an array:
import Foundation
let path = Bundle.main.path(forResource: "Lorem Ipsum", ofType: "txt")!
let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
let lines = text.components(separatedBy: CharacterSet.newlines)
print(lines)
As an alternative, you can use the overloading of components(separatedBy:) that takes a parameter of type String. The code sample below shows how to use it:
import Foundation
let path = Bundle.main.path(forResource: "Lorem Ipsum", ofType: "txt")!
let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
let lines = text.components(separatedBy: "\n")
print(lines)
⚠️ You should however prefer the overloading of components(separatedBy:) that takes a CharacterSet parameter and use it with the value CharacterSet.newlines as this will manage all new line characters (U+000A ~ U+000D, U+0085, U+2028, and U+2029).
#2. Using StringProtocol's enumerateSubstrings(in:options:_:) method
Foundation provides String a method called enumerateSubstrings(in:options:_:). The code sample below shows how to use enumerateSubstrings(in:options:_:) with options parameter value set to String.EnumerationOptions.byLines in order to load the content of a text file line by line into an array:
import Foundation
let path = Bundle.main.path(forResource: "Lorem Ipsum", ofType: "txt")!
let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
let range = text.startIndex ..< text.endIndex
var lines = [String]()
text.enumerateSubstrings(in: range, options: String.EnumerationOptions.byLines) {
(substring, range, enclosingRange, stop) in
guard let substring = substring else { return }
lines.append(substring)
}
print(lines)
#3. Using NLTokenizer's enumerateTokens(in:using:) method
NLTokenizer has a method called enumerateTokens(in:using:). enumerateTokens(in:using:) has the following declaration:
#nonobjc func enumerateTokens(in range: Range<String.Index>, using block: (Range<String.Index>, NLTokenizer.Attributes) -> Bool)
Enumerates over a given range of the string and calls the specified block for each token.
The code sample below shows how to use enumerateTokens(in:using:) in order to load the content of a text file line by line into an array:
import Foundation
import NaturalLanguage
let path = Bundle.main.path(forResource: "Lorem Ipsum", ofType: "txt")!
let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
let tokenizer = NLTokenizer(unit: .paragraph)
tokenizer.setLanguage(.english)
tokenizer.string = text
var lines = [String]()
tokenizer.enumerateTokens(in: text.startIndex ..< text.endIndex) { (range, attributes) -> Bool in
let line = String(text[range])
lines.append(line)
return true
}
print(lines)
Swift 3 version based on the accepted answer:
func arrayFromContentsOfFileWithName(fileName: String) -> [String]? {
guard let path = Bundle.main.path(forResource: fileName, ofType: "txt") else {
return nil
}
do {
let content = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
return content.components(separatedBy: "\n")
} catch {
return nil
}
}
If you are in Swift 2.0, you should use:
let path = NSBundle.mainBundle().pathForResource(fileName, ofType: nil)
if path == nil {
return nil
}
var fileContents: String? = nil
do {
fileContents = try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
} catch _ as NSError {
return nil
}
This works only until Xcode 6.1 beta 1. In 6.1 beta 2 you must write this:
var err: NSError? = NSError()
let s = String(contentsOfFile: fullPath, encoding: NSUTF8StringEncoding, error: &err)
Where fullPath is a string containing the full path to the file and NSUTF8StringEncoding is a predefined constant for UTF8-Encoding.
You can also use NSMacOSRomanStringEncoding for Mac files or NSISOLatin1StringEncoding for Windows files.
s is an optional String and you can look if reading the file was successful:
if (s != nil)
{
return (s!) // Return the string as "normal" string, not as optional string
}
My simple coding for you
let path = NSBundle.mainBundle().pathForResource("FileName", ofType: "txt")
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)
var array = text.componentsSeparatedByString("\n")
Swift 3.0
if let path = Bundle.main.path(forResource: <#FileName#>, ofType: "txt")
{
do
{
let str = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
return str.components(separatedBy: "\n")
}
catch
{
}
}
else
{
return nil
}
For me works as follow:
let myFileURL = NSBundle.mainBundle().URLForResource("listacomuni", withExtension: "txt")!
let myText = try! String(contentsOfURL: myFileURL, encoding: NSISOLatin1StringEncoding)
print(String(myText))
If you want to read a csv file of numeric data. (based on Cezar's answer)
func get_csv_data() -> [[Double]] {
guard let path = NSBundle.mainBundle().pathForResource("filename_without_extension", ofType: "csv") else {
return []
}
do {
let content = try String(contentsOfFile:path, encoding: NSUTF8StringEncoding)
let line_str_array = content.componentsSeparatedByString("\n")
return line_str_array.map {
let field_str_array = $0.componentsSeparatedByString(",")
return field_str_array.map {
Double($0)!
}
}
} catch _ as NSError {
return []
}
}