Whenever I do println(error.localizedDescription) I get something that says :
Optional("description of the error here")
Rather than just:
"description of the error here"
How do I get rid of the Optional() part of the description?
I tried the below method which results in a compiler error saying that it's not an optional.
func performLoginRequestWithURL(url: NSURL, email: String, password: String) {
let bodyData = "email=\(email)&password=\(password)"
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": errString])
} else if data != nil {
let json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
if let dictionary = JSON().parseJSON(json) as [String: AnyObject]? {
let accesstoken = dictionary["id"] as! String
let id = dictionary["userId"] as! Int
var results = [String: AnyObject]()
results = ["at": accesstoken, "id": id]
// MARK: - Store UID & AccessToken
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "userLoggedIn")
NSUserDefaults.standardUserDefaults().setInteger(id, forKey: "userId")
NSUserDefaults.standardUserDefaults().setObject(accesstoken, forKey: "accessToken")
NSUserDefaults.standardUserDefaults().synchronize()
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": "Success"])
}
}
}
}
The compiler is correct - error is an optional but error.localizedDescription is not. You either need to unwrap the error first, safely by
if let unwrappedError = error {
println(unwrappedError.localizedDescription)
}
or: Use optional chaining - https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
let description = error?.localizedDescription
which will either cause 'description' to be the string of localizedDescription, if the error optional can be unwrapped successfully, or possibly 'nil' if not.
Code taken from example added to question. Seems to work as ok, below code can be copied into playground to show error and data string printout - just add valid http:// address into see valid return or try with just 'http:' to get an error.
import UIKit
import XCPlayground
func performLoginRequestWithURL(url: NSURL) {
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
print(errString)
} else if data != nil {
let json = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
print(json)
}
}
}
performLoginRequestWithURL(NSURL(string:"http:")!)
XCPSetExecutionShouldContinueIndefinitely()
You have to unwrap error using ?:
if let errString = error?.localizedDescription {
println(errString)
}
If you already know from an earlier check that error isn't nil, you can force unwrap:
println(error!.localizedDescription)
This will crash if error is nil, so use the ! syntax with caution.
Related
I am trying to fetch data from an api where the JSON returned has URLs to other pieces of information that I need, such as
"value1" : "data",
"value2": {
"url": "https://example.com/stuff",
}
My logic is as follows:
func(completion: #escaping ([Data]) -> ()) {
var classArray = [myClass]()
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
guard let resultArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
let myObject = myClass(value1: resultArray["value1"]! as! String)
guard let valueUrl = URL(string: resultArray["value2"]! as! String) else { return }
URLSession.shared.dataTask(with: valueUrl) { (data, _, _) in
myObject.value2 = data
classArray.append(myObject)
}.resume()
} catch let error {
print("Failed to create json with error: ", error.localizedDescription)
}
completion(classArray)
}.resume()
}
}
Is this a valid approach or are there better implementations? Trying to avoid a future Pyramid of Doom situation. I have tried putting the inner URLSession call in a separate private function but still receive an empty classArray in the end.
I am trying to fetch some record from some entity , but when trying to fetch frequently i am getting Bad Access error ,and app is crashing . please help .
var mContext:NSManagedObjectContext! = appDelegate.persistentContainer.viewContext
func getAllRoomName() -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
do {
if let arrSwitchesMo = try? mContext.fetch(fetchRequest) as? [SwitchMO]
{
for switchMo in arrSwitchesMo ?? []
{
arrRoomNames.append(switchMo.roomName ?? "")
}
}
} catch {
print("Error with request: \(error)")
}
arrRoomNames = Array(Set(arrRoomNames))
return arrRoomNames;
}
Bad Access Error
How can i get rid of this , Please help me .
If you are using a specific fetch request a type cast is redundant. And if you are using do catch don't try?
func getAllRoomName() -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
do {
let arrSwitchesMo = try mContext.fetch(fetchRequest)
for switchMo in arrSwitchesMo {
arrRoomNames.append(switchMo.roomName ?? "")
}
arrRoomNames = Array(Set(arrRoomNames))
} catch {
print("Error with request: \(error)")
}
return arrRoomNames
}
However you should make a function can throw if this function on its part contains a throwing function
func getAllRoomName() throws -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
let arrSwitchesMo = try mContext.fetch(fetchRequest)
for switchMo in arrSwitchesMo {
arrRoomNames.append(switchMo.roomName ?? "")
}
return Array(Set(arrRoomNames))
}
If the code still crashes then the managed object context is nil. Declare the context non-optional as suggested in the Core Data template.
I need to insert records to the coredata for 2 entities from one push notification, when application is in background, but when I try to insert, it stops execution while fetching data from existing records and continues after another notification occurs or when user clicks on notification.
I want data to be inserted when the user did not click on the push notification and when app is in background state.
Here is my code part to fetch record and to insert:
static func insertFromNotificationMessage(_ context: NSManagedObjectContext, message: [AnyHashable: Any]) -> myData{
var myData : myData?
context.performAndWait {
myData = NSEntityDescription.insertNewObject(
forEntityName: "myData", into: context) as? myData
myData?.guid = message["id"] as? String
myData?.title = message["ttl"] as! String
let anotherData = anotherData.getByUUID(context, UUID: message["id"] as! String) ?? anotherData.insertFromNotificationMessage(context, details: message)
myData?.author = author
do{
try context.saveContextAndWait()
}catch let error{
print("Error\(error)")
}
}
return myData!
}
static func getByUUID(_ context: NSManagedObjectContext, UUID : String)-> anotherData?{
let fetchSingleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "anotherData")
fetchSingleRequest.predicate = NSPredicate(format: "guid =[c] %#", UUID)
do{
let fetchedUser = try context.fetch(fetchSingleRequest) as! [anotherData]
if fetchedUser.count > 0{
return fetchedUser.first
}
}catch{
print("Failed to fetch: \(error)")
}
return nil
}
Do it in the app delegate:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
YOUR OBJECT CLASS.saveMessage(userInfo) // code to save into core data
completionHandler(.NewData)
}
Here is my example function to save the data
static func saveMessage(userInfo: [NSObject : AnyObject]) -> Message {
let alertInfo = userInfo["aps"]?["alert"]!
let title:String?
let body:String?
if let alertTitle = alertInfo!["title"] as? String{
title = alertTitle
body = alertInfo!["body"] as? String
} else {
title = userInfo["title"] as? String ?? "CUSTOM TITLE"
body = alertInfo as? String
}
let message = Message(date: NSDate(), title: title!, message: body!, isReaded: false)
DBUtil.saveContext()
return message
}
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)
}
}
I had this problem, you can't use encoding: .utf8 for unpredictable data. It will return nil every time.
Use this instead:
String(decoding: data, as: UTF8.self)
For anyone coming in future (which are probably not interested in OP's film code?!);
Simply, try something like:
extension Data {
public func toString() -> String {
return String(data: self, encoding: .utf8) ?? "";
}
}
See also my toHex related answer
public convenience init(nsurl:NSURL) {
var enc:NSStringEncoding = NSUTF8StringEncoding
var err:NSError?
let str:String? =
NSString(
contentsOfURL:nsurl, usedEncoding:&enc, error:&err
)
if err != nil { self.init(err!) }
else { self.init(string:str!) }
}
Swift is version 1.2, error message is:
NSString? is not convertible to string
With Swift 1.2 automatic bridging between String and NSString has been removed.
So you have to explicitly do the cast :
let str = NSString(contentsOfURL:nsurl, usedEncoding:&enc, error:&err) as? String
Swift's String accepts this same initializer as NSString, so you don't even have to use NSString nor to typecast:
let str = String(contentsOfURL: nsurl, encoding: enc, error: &err)
Update for Swift 2.0
do {
let str = try String(contentsOfURL: nsurl, encoding: enc)
print(str)
} catch {
print(error)
}
You can also use an Optional with try? if you want, in this case no need for do catch:
if let str = try? String(contentsOfURL: nsurl, encoding: enc) {
print(str)
} else {
// didn't succeed
}
In Swift 3 I am using the following
do{
let str = try String(contentsOf:personUrl!)
print(str)
}catch let error{
print(error)
}