0

I have Obj-C code that looks like this

NSURL * url = [[NSBundle mainBundle] URLForResource:@"MyFile" withExtension:@"txt"];
NSError * err = nil;
NSString * string = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&err];

This code is working properly. I need to convert it to Swift one, so I did it like this

func getContentOfUrl(by fileName: String) -> String? {
    var result: String?
    guard let pathToFile = Bundle.main.path(forResource: fileName, ofType: "txt")
        else {
            return nil
        }
      
      do {
        result = try String(contentsOf: URL(string: pathToFile)!, encoding: String.Encoding.utf8)
      }
      catch let err{
      }
      
      return result
    }

And I get an error in the catch block

Attempt to get content of URL FAILED, error: Error Domain=NSCocoaErrorDomain Code=256 "The file “MyFile.txt” couldn’t be opened." UserInfo={NSURL=/private/var/containers/Bundle/Application/63FB01-F11-411-B93-6578DEF57B/MyApp.app/MyFile.txt}

what am I doing wrong?

3
  • 1
    guard let fileURL = Bundle.main.url(forResource: fileName, ofType: "txt") and then then use this url rather than that path, e.g., String(contentsOf: fileURL, encoding: .utf8). Commented Jul 19, 2021 at 12:44
  • 2
    Never create an URL from a file system path with URL(string:. This is the reason of the error. The proper API is URL(fileURLWithPath. And – as already pointed out – URLForResource:withExtension: exists also in Swift. Commented Jul 19, 2021 at 12:57
  • 1
    Also, be really careful with all of the contentsOf: url APIs on String, Data, NSDictionary, NSArray, etc. They're synchronous and can block for awhile. Commented Jul 19, 2021 at 13:29

2 Answers 2

2

This works for me.

func getFile(named: String) -> String? {
    guard let url = Bundle.main.url(forResource: named, withExtension: "txt") else { return nil }
    return try? String(contentsOf: url, encoding: .utf8)
}

If it doesn't you need to make sure that the resource file actually exists.

You can test little functions like this in a playground. Add the file you want to use to the Resources folder in the playground. I did this with a file name "MyText.txt" and this worked fine.

Sign up to request clarification or add additional context in comments.

Comments

2

Apart from the fact that you should use a method that returns a URL instead of a file path as already mentioned is that you are using the wrong init method when creating the URL yourself. URL(string:) expects a string in a url format (file://...) but you have a local path so instead you should use URL(fileURLWithPath:). So this will make your existing code work

result = try String(contentsOf: URL(fileURLWithPath: pathToFile), encoding: .utf8)

Or use the URL directly

func content(of fileName: String) -> String? {
    Bundle.main.url(forResource: fileName, withExtension: "txt")
        .flatMap { try? String.init(contentsOf: $0, encoding: .utf8) }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.