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)
}
Related
I have a question:
I'm retrieving a long string made of some base 64 strings attached together with ";" separating each of them inside said string.
Here's my code:
if(item.photo != "null"){
let b64fullstring = item.photo
if(b64fullstring!.contains(";")){
let photos = b64fullstring!.split(separator: ";")
for pic in photos{
let base64encodedstring = pic
let decodedData = Data(base64Encoded: base64encodedstring!, options: Data.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedString = String(data: decodedData, encoding: .utf8)!
print(pic)
}
}
}
Its gives me the following error on the "data" function;
Type of expression is ambiguous without more context
I really don't get it.
When working on a single string, it works perfectly fine. But when using a loop, it gives this message for some reason.
Thank you for taking some of your time for helping me.
Swift errors are not very helpful. The problem there is that split method returns an array of substrings:
func split(separator: Character, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true) -> [Substring]
And the Data initializer expects a String:
init?(base64Encoded base64String: String, options: Data.Base64DecodingOptions = [])
You just need to initialize a new string from your substring:
if let photos = b64fullstring?.split(separator: ";") {
for pic in photos {
if let decodedData = Data(base64Encoded: String(pic), options: .ignoreUnknownCharacters) {
if let decodedString = String(data: decodedData, encoding: .utf8) {
print(pic)
}
}
}
}
Another option is to use components(separatedBy:) method which returns an array of strings instead of substrings:
func components<T>(separatedBy separator: T) -> [String] where T : StringProtocol
if let photos = b64fullstring?.components(separatedBy: ";") {
for pic in photos {
if let decodedData = Data(base64Encoded: pic, options: .ignoreUnknownCharacters) {
if let decodedString = String(data: decodedData, encoding: .utf8) {
print(pic)
}
}
}
}
How can I properly handle optionals especially when it is multi-level optional?
For example,
let html = String(data: response.data!, encoding: .utf8)
if let string = html {
let doc = try? SwiftSoup.parse(html)
let links = try? doc?.select("a").array().map{try? $0.attr("href")}
}
// links!![0]! is crazy
Constant links is like below: optional of optional array of optional strings
Optional(Optional([Optional("abc.com"), Optional("def.com")]))
Is there a pattern which is more proper than do-try-catch block or optional binding?
Replace most of the try? with try in a do/catch. You only need one do/catch with your multiple try. Use if let more. Use flatMap instead of map.
if let html = String(data: response.data!, encoding: .utf8) {
do {
let doc = try SwiftSoup.parse(html)
let links = try doc.select("a").array().flatMap { try? $0.attr("href") }
} catch {
// Uh-oh
}
}
I can't test this so this may not be perfect but it should get you most of the way there.
You can chain several if let statements in a single command, like this:
if let string = html, let doc = try? SwiftSoup.parse(html) {
...
}
You've got several choices, none of which are really more "proper" than the others, so it's a matter of taste. But I'd probably do one of:
A compound if statement:
if let responseData = response.data,
let string = String(data: responseData, encoding: .utf8),
let doc = try? SwiftSoup.parse(html),
let links = try? doc.select("a").array().compactMap({ try? $0.attr("href") }) {
// do something with links, any nil will get filtered out from the array
// (if using earlier than Swift 4.1, use flatMap instead of compactMap)
}
guard statements:
guard let responseData = response.data else { /* bail somehow */ }
guard let string = String(data: responseData, encoding: .utf8) else { /* bail somehow */ }
... etc ...
Alternately, one long guard statement written like the if statement above.
Or, y'know, just use a good old-fashioned do/try/catch block.
My question is i want to hit the url and when i hit the url on server side the php return the results just echo in php and i have to save that result in variable in swift 3, i tried the below code:
let URLstr = URL(string: strURL)
let request = URLRequest(url: URLstr!)
request.httpMethod = "POST"
print (request)
I didn't get the content of URL in swift which is much easier in objective C.
Use the string initializer with the url.
do {
let contents = try String(contentsOf: URLstr, encoding: .ascii)
} catch {
// handle error
}
Or you can use URLSession.
let task = URLSession.shared.dataTask(with: URLStr) { data, response, error in
guard data != nil else { // no data }
let contents = String(data: data!, encoding: .ascii)
}
task.resume()
I adjusted the code provided above. To fetch HTML code use URLSession.
Swift 5
let session = URLSession.shared
let url = URL(string: "https://yourwebsiteaddress.com/")!
let task = session.dataTask(with: url) { data, response, error in
// Check whether data is not nil
guard let loadedData = data else { return }
// Load HTML code as string
let contents = String(data: loadedData, encoding: .utf8)
print(contents)
}
task.resume()
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)
}
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")
}