try-catch placement in converting Swift dictionary to JSON string - swift

Here's what I've got:
do {
try let jsonData: NSData = NSJSONSerialization.dataWithJSONObject(paramsDict, options: NSJSONWritingOptions.PrettyPrinted)
jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
} catch {
print("CAUGHT SOMETHING session token")
}
I'm getting an error try must be placed on the initial value expression. I tried 'rephrasing' like so:
do {
let jsonData: NSData = NSJSONSerialization.dataWithJSONObject(paramsDict, options: NSJSONWritingOptions.PrettyPrinted)
try jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
} catch {
print("CAUGHT SOMETHING session token")
}
but this leads to an error Call can throw but is not marked with 'try'. How should I be structuring this try-catch and what do these error codes mean?

You have to change the location of where you are putting your try.
do {
if let jsonData: NSData = try NSJSONSerialization.dataWithJSONObject(paramsDict, options: NSJSONWritingOptions.PrettyPrinted) {
//is jsonString a variable you have previously declared?
//if not, put "if let" before it, because you are creating it IF:
//your "try" - attempt to get data from json succeeds
jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
}
} catch {
print("CAUGHT SOMETHING session token")
}

Related

Initiating string with content of file (Swift)

Swift newbie here. I am trying load a text file into a string using the following code:
var uncondString: String
if let tempstring = try? String(contentsOf: url, encoding: .utf8) {
uncondString = tempstring
}
print("\(uncondString)")
The print statement, however, throws error "Variable 'uncondString' used before being initialized"
I guess this is trivial for more experienced users but any help is appreciated.
If the statement try? fails, it returns nil, and the if let statement is not executed when nil is returned
After all, this code is not a code that unconditionally succeeds in initialization, so a warning is displayed.
var uncondString: String
if let tempstring = try? String(contentsOf: url, encoding: .utf8) {
uncondString = tempstring
print(uncondString)
}
or
let uncondString: String? = try? String(contentsOf: url, encoding: .utf8)
if let uncondStr = uncondString {
print(uncondStr)
}

Writing Log Text File-Should I create an Array to store Filenames or Keep the File Open [duplicate]

I am trying to append a string into text file. I am using the following code.
let dirs : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if (dirs) != nil {
let dir = dirs![0] //documents directory
let path = dir.stringByAppendingPathComponent("votes")
let text = "some text"
//writing
text.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
//reading
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
println(text2) //prints some text
}
this does not append the string to file. Even if I call this function repeatedly.
If you want to be able to control whether to append or not, consider using OutputStream. For example:
do {
let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("votes.txt")
guard let outputStream = OutputStream(url: fileURL, append: true) else {
print("Unable to open file")
return
}
outputStream.open()
let text = "some text\n"
try outputStream.write(text)
outputStream.close()
} catch {
print(error)
}
By the way, this is an extension that lets you easily write a String (or Data) to an OutputStream:
extension OutputStream {
enum OutputStreamError: Error {
case stringConversionFailure
case bufferFailure
case writeFailure
}
/// Write `String` to `OutputStream`
///
/// - parameter string: The `String` to write.
/// - parameter encoding: The `String.Encoding` to use when writing the string. This will default to `.utf8`.
/// - parameter allowLossyConversion: Whether to permit lossy conversion when writing the string. Defaults to `false`.
func write(_ string: String, encoding: String.Encoding = .utf8, allowLossyConversion: Bool = false) throws {
guard let data = string.data(using: encoding, allowLossyConversion: allowLossyConversion) else {
throw OutputStreamError.stringConversionFailure
}
try write(data)
}
/// Write `Data` to `OutputStream`
///
/// - parameter data: The `Data` to write.
func write(_ data: Data) throws {
try data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) throws in
guard var pointer = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
throw OutputStreamError.bufferFailure
}
var bytesRemaining = buffer.count
while bytesRemaining > 0 {
let bytesWritten = write(pointer, maxLength: bytesRemaining)
if bytesWritten < 0 {
throw OutputStreamError.writeFailure
}
bytesRemaining -= bytesWritten
pointer += bytesWritten
}
}
}
}
For Swift 2 rendition, see previous revision of this answer.
You can also use FileHandle to append String to your text file. If you just want to append your string the end of your text file just call seekToEndOfFile method, write your string data and just close it when you are done:
FileHandle usage Swift 3 or Later
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// create a new text file at your documents directory or use an existing text file resource url
let fileURL = documentsDirectory.appendingPathComponent("simpleText.txt")
do {
try Data("Hello World\n".utf8).write(to: fileURL)
} catch {
print(error)
}
// open your text file and set the file pointer at the end of it
do {
let fileHandle = try FileHandle(forWritingTo: fileURL)
fileHandle.seekToEndOfFile()
// convert your string to data or load it from another resource
let str = "Line 1\nLine 2\n"
let textData = Data(str.utf8)
// append your text to your text file
fileHandle.write(textData)
// close it when done
fileHandle.closeFile()
// testing/reading the file edited
if let text = try? String(contentsOf: fileURL, encoding: .utf8) {
print(text) // "Hello World\nLine 1\nLine 2\n\n"
}
} catch {
print(error)
}
Please check the below code as its working for me. Just Add the code as it is:
let theDocumetFolderSavingFiles = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let filePath = "/theUserData.txt"
let thePathToFile = theDocumetFolderSavingFiles.stringByAppendingString(filePath)
let theFileManager = NSFileManager.defaultManager()
if(theFileManager.fileExistsAtPath(thePathToFile)){
do {
let stringToStore = "Hello working fine"
try stringToStore.writeToFile(thePathToFile, atomically: true, encoding: NSUTF8StringEncoding)
}catch let error as NSError {
print("we are geting exception\(error.domain)")
}
do{
let fetchResult = try NSString(contentsOfFile: thePathToFile, encoding: NSUTF8StringEncoding)
print("The Result is:-- \(fetchResult)")
}catch let errorFound as NSError{
print("\(errorFound)")
}
}else
{
// Code to Delete file if existing
do{
try theFileManager.removeItemAtPath(thePathToFile)
}catch let erorFound as NSError{
print(erorFound)
}
}
A simple solution that works for me. UPDATE, it looks like I must have gotten this from here, so credit where credit is due:
Append text or data to text file in Swift
Usage:
"Hello, world".appendToURL(fileURL: url)
Code:
extension String {
func appendToURL(fileURL: URL) throws {
let data = self.data(using: String.Encoding.utf8)!
try data.append(fileURL: fileURL)
}
}
extension Data {
func append(fileURL: URL) throws {
if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
defer {
fileHandle.closeFile()
}
fileHandle.seekToEndOfFile()
fileHandle.write(self)
}
else {
try write(to: fileURL, options: .atomic)
}
}
}
Check the reading part.
The method cotentsOfFile: is a method of NSString class. And you have use it wrong way.
So replace this line
let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
Here you have to use NSString instead of String class.
let text2 = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)

How can I convert a string, such as "iso-8859-1", to it's String.Encoding counterpart?

After sending a HTTP request from Swift, I get a field in the response called textEncodingName.
I want to convert the data object I also received into a string containing its contents, and to do this, I'm using String(data: data!, encoding: .utf8). This works most of the time, because most websites are UTF-8 encoded. But with, for example, https://www.google.co.uk, the response.textEncodingName == "iso-8859-1".
I guess other websites would use even more obscure encodings, so my question is this: how can I find the right encoding to convert my data object to the correct string.
You can simply try String.Encoding.windowsCP1250 for iso-8859-1. Please refer https://en.wikipedia.org/wiki/Windows-1250
String(data: data, encoding: .windowsCP1250)
OR..
I found a few steps that will take you from the textEncodingName to the corresponding String.Encoding value:
let estr = "iso-8859-1"
let cfe = CFStringConvertIANACharSetNameToEncoding(estr as CFString)
let se = CFStringConvertEncodingToNSStringEncoding(cfe)
let encoding = String.Encoding(rawValue: se)
This is largely based on the documentation for URLResponse.textEncodingName:
You can convert this string to a CFStringEncoding value by calling CFStringConvertIANACharSetNameToEncoding(:). You can subsequently convert that value to an NSStringEncoding value by calling CFStringConvertEncodingToNSStringEncoding(:).
Here's an update that checks to see if the original text encoding string is valid or not:
let estr = "XXX"
let cfe = CFStringConvertIANACharSetNameToEncoding(estr as CFString)
if cfe != kCFStringEncodingInvalidId {
let se = CFStringConvertEncodingToNSStringEncoding(cfe)
let sse = String.Encoding(rawValue: se)
print("sse = \(sse)")
} else {
print("Invalid")
}
I would write an enum with a String raw value and a computed property to return the appropriate String.Encoding value. Then you can use its init(rawValue:) to create an instance.
import Foundation
enum APITextEncoding : String
{
case iso8859_1 = "iso-8859-1"
// etc.
var encoding: String.Encoding
{
switch self
{
case .iso8859_1:
return .isoLatin1
// etc.
}
}
}
let receivedEncoding = APITextEncoding(rawValue: encodingDescription)
let receivedText = String(data: receivedData, encoding: receivedEncoding.encoding)
In Swift You can use:
guard let string = String(data: data, encoding: .isoLatin1) else {return}
guard let perfectData = string.data(using: .utf8, allowLossyConversion: true) else {return}
In swift you can use:
func getTextFrom(_ url: URL) -> String? {
guard let data = try? Data(contentsOf: url) else {
return nil
}
return String(data: data, encoding: .utf8) ??
String(data: data, encoding: .isoLatin1)
}

PerformSegueWithIdentifier if parameter values are correct for http POST request

When the parameter (params) values are incorrect, it still login to another view. In the console, both response body and response header return values when printed. Where have I gone wrong?
func login() {
let request = NSMutableURLRequest(URL: NSURL(string: "http://someurl/verify/")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params =
[
"username":username.text!,
"password":password.text!,
"deviceid":"r49rvnjff",
"method":"method",
"payload":"payload"
]
as Dictionary<String,String>
print(params)
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted)
}
catch {
print(error)
return
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if error == nil {}
let json: NSDictionary?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
}
catch let dataError {
print(dataError)
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
return
}
if let parseJSON = json {
let authenticated = parseJSON["authenticated"] as? String
print("authenticated:\(authenticated)")
if authenticated != "False" {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("segue", sender: self)
}
}
}
else {
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: \(jsonStr)")
}
})
task.resume()
}
Thanks in advance!
What does your invalid response look like? might it be that its returning "false" and not "False" (which is what you are checking for), in any case I would recommend verifying your response from the server so that you can have the authenticated parameter be a boolean, so you can unwrap your optional as so:
if let authenticated = json["authenticated"] as? Bool {
if (authenticated)
{
// ...
}
}
Also, I noticed that your else block will never be hit because your json property is unwrapped. Your serialization is successful so json is not nil, hence the if let parseJSON = json will always work.
I would suggest the following approach:
if let authenticated = json["authenticated"] as? Bool
{
if (authenticated)
{
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("segue", sender: self)
}
}
else
{
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: \(jsonStr)")
}
}
Good luck!

How do I find out the exact error in catch block

I have this swift beauty:
do {
let path = "/Users/ADMIN/Desktop/hello.txt"
let str = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
print(str)
} catch {
print("shit happens")
}
Of course I am getting the error message because there's an error. How can I find out what the error is? (yes, the file exists)
Thanks
Make catch create an "error" constant for you, compatible with NSError:
do {
let path = "/Users/ADMIN/Desktop/hello.txt"
let str = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
print(str)
} catch let error as NSError {
print(error.localizedDescription)
}
You can also just use catch which automatically creates an "error" constant:
do {
let path = "/Users/ADMIN/Desktop/hello.txt"
let str = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
print(str)
} catch {
print(error)
}