String.init(contentsOfFile:) replacement for Linux? - swift

After deploying my Swift 3 app to Heroku, it crashed with the following error:
fatal error: init(contentsOfFile:usedEncoding:) is not yet implemented: file Foundation/NSString.swift, line 1255
What can I use instead of String.init(contentsOfFile:) on Ubuntu?

Seeing the latest source code of Swift Standard Library, String.init(contentsOfFile:) internally calls NSString.init(contentsOfFile:usedEncoding:). (NSStringAPI.swift)
And the Linux version of NSString.init(contentsOfFile:usedEncoding:), as you see, is not implemented yet. (NSString.swift)
Seems NSString.init(contentsOfFile:encoding:) is already implemented and String.init(contentsOfFile:encoding:) calls it. So, if you know the encoding of the file, use String.init(contentsOfFile:encoding:) like:
let fileContent = try? String(contentsOfFile: filePath, encoding: .utf8)
If you do not know the string encoding of the file, you may need to implement the functionality by yourself.

Related

Swift. How to download a file with http URL line by line without loading full file to memory [duplicate]

In the "Platforms State of the Union" video of WWDC2021 at 28:00 it was mentioned that
[Apple] even added support for asynchronously iterating line-by-line through a file
in Foundation for macOS 12/iOS 15 and Swift 5.5.
What is that new API, how can I now asynchronously iterate line-by-line through a file?
The main thing they added that enables this, is AsyncSequence. AsyncSequence is like Sequence, but its Iterator.next method is async throws.
Specifically, you can use URLSession.AsyncBytes.lines to get an AsyncSequence of the lines in a file.
Suppose you are in an async throws method, you can do:
let (bytes, response) = try await URLSession.shared.bytes(from: URL(string: "file://...")!)
for try await line in bytes.lines {
// do something...
}
Note that there is also FileHandle.AsyncBytes.lines, but in the documentation it says:
Rather than creating a FileHandle to read a file asynchronously, you can instead use a file:// URL in combination with the async-await methods in URLSession. These include the bytes(for:delegate:) and bytes(from:delegate:) methods that deliver an asynchronous sequence of bytes, and data(for:delegate:) and data(from:delegate:) to return the file’s entire contents at once.

Can't access .csv data using MLDataTable

I am trying to read the content of a .csv file using the framework CreateML to read csv data.
Fhe following code generates an error even though the file exists:
let csvURL = URL(fileURLWithPath: "/Volumes/MAC HDD/Data/Data.csv")
let fm = FileManager()
if (fm.fileExists(atPath: csvURL.path)) {
let dataTable = try! MLDataTable(contentsOf: csvURL)
// accessing first column
let col_1 = Array.init(dataTable["col1"])
}
I get the following error message:
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: CreateML.MLCreateError.generic(reason: "No files corresponding to the specified path (file:///Volumes/MAC%20HDD/Data/Data.csv)")
I have checked nearly everything but can't get any results. What am I doing wrong?
I found out by my self what the problem is.
I have implemented this code in a Xcode project to read csv data, but the CreateML framework is just working for Xcode Playground and not within a Xcode Project! It was mentioned at the end of the WWDC 2018 session video 703.
The code example above is working fine with Xcode Playground.
It would have saved me a lot of time if there had been a warning when importing the framework.

I can't use Process in a function

I have a problem I would like to use the function Process() but when I call it in a function I get
Use of unresolved identifier 'Process'
But when I call it directly in my code it works.
Someone knows why?
As you neither mentioned which Platform you are targeting to nor provided any kind of code, you'll need to take note of the following things:
Process() was renamed to CommandLine() in Swift 3.0
If you are targeting iOS, CommandLine() isn't available, so you'll need to do the following:
Using NSTask() on iOS:
The best way to achieve this, or at least the one I prefer the most (also I didn't find any other way to do this), is to use a custom Objective-C header file which creates the object NSTask() and everything it needs.
Then, in order to use this code with Swift, you'll need to create a Bridging-Header, in which you'll need to import the NSTask.h for it to be exposed to Swift and being able to use it in your Swift code.
Once done this, just use the following function in your code whenever you want to run a task:
func task(launchPath: String, arguments: String...) -> NSString {
let task = NSTask.init()
task?.setLaunchPath(launchPath)
task?.arguments = arguments
// Create a Pipe and make the task
// put all the output there
let pipe = Pipe()
task?.standardOutput = pipe
// Launch the task
task?.launch()
task?.waitUntilExit()
// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
return output!
}
And call it like this:
task(launchPath: "/usr/bin/echo", arguments: "Hello World")
This will also return the value, so you can even display it by doing:
print(task(launchPath: "/usr/bin/echo", arguments: "Hello, World!"))
Which will print:
~> Hello, World!
For this to work and not throwing an NSInternalInconsistencyException, you'll need to set the launchPath to the executable's full path instead to just the directory containing it.
You'll also need to set all the command arguments separated by commas.
Tested on both iPad Mini 2 (iOS 12.1 ~> Jailbroken) and iPhone Xr (iOS 12.2 ~> not jailbroken).
NOTE: Even though this works both on non-jailbroken and jailbroken devices, your App will be rejected on the AppStore, as #ClausJørgensen said:
You're using private APIs, so it'll be rejected on the App Store. Also, Xcode 11 has some new functionality that will trigger a build failure when using certain private APIs.
I'd only recommend the usage of this for apps that won't be uploaded to the App Store, otherwise, try to achieve what you want without using commands, there sure will be any other way to do that.
If your app is targeting jailbroken iOS devices and will be uploaded to a third-party store like Cydia, Zebra, Thunderbolt or Sileo, then this would work correctly.

What could be a reason for a do try catch issue with contentsOfFile in Swift 3?

I recently converted the syntax of a project I was working on from Swift 2.3 to Swift 3 and am running into some issues with taking the contents of a txt file and converting them into a string. Everything works fine on old txt files in my project but new txt files are failing in the try portion of my code.
I'm not super familiar with how try/catch works but I've tested the code and on files that don't work the catch portion of my code is running so I'm assuming that the try part is failing in those cases. Is this some sort of bug or something that changed in Swift 3? I've checked the new files and they are txt files, formatted exactly the same way as the working txt files so I'm not sure what is wrong.
if let filepath = Bundle.main.path(forResource: fileName, ofType: "txt") {
do {
contents = try String(contentsOfFile: filepath, encoding: String.Encoding.utf8)
print(contents)
textView.text = contents
} catch {
// contents could not be loaded
print(error)
}
} else {
print("No contents found")
}
Error Message with encoding parameter:
Error Domain=NSCocoaErrorDomain Code=261 "The file “35 IAC 14 - Indiana State Teachers' Retirement Fund.txt” couldn’t be opened using text encoding Unicode (UTF-8)." UserInfo={NSFilePath=/Users/graysonfaircloth/Library/Developer/CoreSimulator/Devices/(removed-numbers)/data/Containers/Bundle/Application/(removed-numbers)/CCHA-test-app.app/35 IAC 14 - Indiana State Teachers' Retirement Fund.txt, NSStringEncoding=4}
Error Message without encoding parameter:
Error Domain=NSCocoaErrorDomain Code=264 "The file “35 IAC 14 - Indiana State Teachers' Retirement Fund.txt” couldn’t be opened because the text encoding of its contents can’t be determined." UserInfo={NSFilePath=/Users/graysonfaircloth/Library/Developer/CoreSimulator/Devices/(removed-numbers)/data/Containers/Bundle/Application/(removed-numbers)/CCHA-test-app.app/35 IAC 14 - Indiana State Teachers' Retirement Fund.txt}
In Swift 3 we have extended using throwing, I think it's better then handling error.
You need to check is your file included into build
and check name of file.
Also you can print your path and check manually is your file added to build (simulator).

Swift Read in UTF8 String from MPMoviePlayerController metadata

I need to read some metadata which can contain French or German characters & accents.
I think that I need to read the metadata as UTF8 String. But being a beginner in Swift and Apple in general I don't know how to do this.
I tried to implement some similar solutions found on stackOverflow but none worked.
What I'm working with:
let firstMeta: MPTimedMetadata = radioPlayer.timedMetadata.first as! MPTimedMetadata
let metaData = firstMeta.value as! String
print(metaData)
There I get
Antonin DvoÅák
instead of
Antonin Dvořák
Any ideas?
Easy solution for how to get UTF8 metadata from online audio stream using AVPlayer
https://stackoverflow.com/a/37831097/4249825
This is a problem in the library, you cannot fix it because the string is already decoded from data.
There is a reason why the whole set of classes was deprecated in iOS 9.
You should use AVPlayerViewController & AVPlayer & AVPlayerItem & AVMetadataItem.
They are solving this issue. Don't use MPMoviePlayerController.
let convertedString = String(data: metaData.dataUsingEncoding(NSISOLatin1StringEncoding)!, encoding: NSUTF8StringEncoding)!