I am very new to Swift.
I want to create something like API on Swift for my educational app.
I have this code:
static func getFilm(filmID: Int) -> String {
print("getFilm")
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
var request = URLRequest(url: url)
var returnData: String = ""
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if var responseVar = response, var dataVar = data {
print(responseVar)
returnData = String(data: dataVar, encoding: .utf8)
} else {
print(error)
}
}
task.resume()
return returnData
}
And I try to convert Data to String in this line: returnData = String(data: dataVar, encoding: .utf8)
Swift compiler gives me an error, and change this line to
returnData = String(data: dataVar, encoding: .utf8)!
, when I execute this line I get empty returnData variable.
If I use basic example line
print(String(data: data, encoding: .utf8))
everything will be OK and I can see data
in XCode console.
So, how I can convert Data to String?
This is an example using a completion handler:
class func getFilm(filmID: Int, completion: @escaping (String) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
print(error!)
completion("")
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(returnData)
} else {
completion("")
}
}
}.resume()
}
And you call it
MyClass.getFilm(filmID:12345) { result in
print(result)
}
In case of an error the completion handler returns an empty string.
MyClass
is the enclosing class of getFilm
method. Most likely the web service will return JSON, so you might need to deserialize the JSON to an array or dictionary.
In a more sophisticated version create an enum with two cases and associated values
enum ConnectionResult {
case success(String), failure(Error)
}
With a little more effort demonstrating the subtle power of Swift you can return either the converted string on success of the error on failure in a single object.
class func getFilm(filmID: Int, completion: @escaping (ConnectionResult) -> ()) {
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
URLSession.shared.dataTask(with:url) { (data, response, error) in
if error != nil {
completion(.failure(error!))
} else {
if let returnData = String(data: data!, encoding: .utf8) {
completion(.success(returnData))
} else {
completion(.failure(NSError(domain: "myDomain", code: 9999, userInfo: [NSLocalizedDescriptionKey : "The data is not converible to 'String'"])))
}
}
}.resume()
}
On the caller side a switch
statement separates the cases.
MyClass.getFilm(filmID:12345) { result in
switch result {
case .success(let string) : print(string)
case .failure(let error) : print(error)
}
}
Meanwhile – with async/await
in Swift 5.5+ – it has become much more comfortable
class func getFilm(filmID: Int) async throws -> String {
print("getFilm")
let url = URL(string: "https://api.kinopoisk.cf/getFilm?filmID=\(filmID)")!
let (data, _) = try await URLSession.shared.data(from: url)
return String(data: data, encoding: .utf8)!
}