swift 3 http request for json file and inserting into fmdb - swift3

I'm having trouble inserting data from the JSON file from my server.
I'm getting the error of
Failed to insert initial data into the database.
Error Domain=FMDatabase Code=1 "near "s": syntax error" UserInfo={NSLocalizedDescription=near "s": syntax error} near "s": syntax error
However, 9 of the data entries are inserted into the database before the error occurs and the app doesn't crash too. I'm wondering if it's a problem with the json file on the server or it's the code that I have. Any suggestions would be much appreciated. I only started 2-3 months ago so perfectionists cut me some slack
func insertMovieData() {
if openDatabase() {
let url = URL(string: "http://blahblahblah")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
print(json)
var query = ""
let jsonObj = JSON(json)
if jsonObj != JSON.null {
for (_, jsonObj) in jsonObj {
let movieTitle = jsonObj["subject"]
let movieCoverURL = jsonObj["message"]
print(movieTitle)
query += "insert into movies values (null, '\(movieTitle)', '\(movieCoverURL)');"
}
}
if !self.database.executeStatements(query) {
print("Failed to insert initial data into the database.")
print(self.database.lastError(), self.database.lastErrorMessage())
}
else {
print(movies)
}
self.database.close()
}
task.resume()
}
}

Related

Cannot fetch multiple CKReference records from public Database in a for loop

I have a contact CKRecord with many location CKRecords ( 1 to many relationship)
Both contact CKRecord and Location CKRecord are created in public Database. I add CKReference fro contact to locaiotn via a field named owningContact on location.
ckRecord["owningContact"] = CKReference(record: contactRecord!, action: .deleteSelf)
I go to cloudKit dashboard and verify both the records exist. The location CKRecord has field owningContact that has the recordName of the contact CKRecord. I defined a function to get locations like this:
private func iCloudFetchLocations(withContactCKRecord: CKRecord, completionHandler: #escaping ([CKRecord]?, Error?) -> Void) {
var records = [CKRecord]()
let recordToMatch = CKReference(recordID: withContactCKRecord.recordID, action: .deleteSelf)
let predicate = NSPredicate(format: "owningContact == %#", recordToMatch)
// Create the query object.
let query = CKQuery(recordType: "location", predicate: predicate)
let queryOp = CKQueryOperation(query: query)
queryOp.resultsLimit = 1
queryOp.qualityOfService = .userInteractive
queryOp.recordFetchedBlock = {
records.append($0)
print($0)
}
queryOp.queryCompletionBlock = { (cursor, error) in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
}
if (cursor != nil) {
let newOperation = CKQueryOperation(cursor: cursor!)
newOperation.resultsLimit = queryOp.resultsLimit
newOperation.recordFetchedBlock = queryOp.recordFetchedBlock
newOperation.queryCompletionBlock = queryOp.queryCompletionBlock
self.publicDB?.add(newOperation)
}
completionHandler(records, error)
}
self.publicDB?.add(queryOp)
}
Then I call the code to fetch location CKRecord based on contact CKRecord like this:
let predicate = NSPredicate(format: "TRUEPREDICATE")
let query = CKQuery(recordType: Cloud.Entity.Contact, predicate: predicate)
publicDB?.perform(query, inZoneWith: nil, completionHandler: { (records, error) in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
completion(false)
}
if let contactRecords = records {
for aContactRecord in contactRecords {
// fetch Location Data
self.iCloudFetchLocations(withContactCKRecord: aContactRecord, completionHandler: { records, error in
guard error == nil else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
return
completion(false)
}
if let locationRecords = records {
}
})
}
}
})
I have two contacts the first one has been CKReferenc'ed to the location, where as the second contact is still not yet CKReferenc'ed to the location.
I think here is the problem: First time in the loop contact CKRecord information is sent by calling iCloudFetchLocations which returns immediately without waiting for cloud response, and the for loop sends the second contact and calls iCloudFetchLocations again. Since the second contact has no CKReference to the location, the call fails and I can never get to the first contact's location since it hasn't returned yet.
How to fix this?
I found that I had not set the CKReference field: owningContact as Queryable. The way I found out is printing error like this: 
if let ckerror = error as? CKError {
print(ckerror.userInfo)
print(ckerror.errorUserInfo)
self.aErrorHandler.handleCkError(ckerror: ckerror)
}
As soon as I did that it started working, Since I was in a for loop it was timing out on previous fetch I think.

Json parsing in Swift 3.0

This is my code for Jason parsing in Swift:
static func POST(url: String, parameters: NSDictionary, completionBlock: #escaping CompletionBlock){
let todoEndpoint: String = Webservices.Base_Url.appending(url)
guard let url = NSURL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
var request = URLRequest(url: url as URL)
//var request = URLRequest(url: NSURL(string: todosEndpoint)! as URL)
let session = URLSession.shared
request.httpMethod = "POST"
var err: NSError?
let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
request.httpBody = jsonData
request.addValue("application/x-www-form-urlencoded;charset=UTF-8 ", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
guard error == nil else {
print("error calling POST on /todos/1")
print(error)
return
}
// make sure we got data
guard let dataTemp = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: dataTemp, options: []) as? [String: AnyObject] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo, let's just print it to prove we can access it
print("The todo is: " , todo)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
I got while jason parsing:
error expression produced error: error: Execution was interrupted,
reason: EXC_BAD_ACCESS (code=1, address=0x0). The process has been
returned to the state before expression evaluation.
What's wrong?

Getting 100x100 profile pic using Facebook API, Firebase and Swift

My project had been getting the URL string for the medium sized profile pic using this code:
let downloadMediumPicTask = session.dataTask(with: mediumProfPictureURL) { (data, response, error)
in
// The download has finished.
if let e2 = error {
print("Error downloading profile picture: \(e2)")
} else {
if let res2 = response as? HTTPURLResponse {
print("Downloaded medium profile picture with response code \(res2.statusCode)")
if let imageData2 = data {
mediumProfilePictureUIImageFile = UIImage(data: imageData2)!
print("mediumProfilePictureUIImageFile has now been defined as: \(mediumProfilePictureUIImageFile).")
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadMediumPicTask.resume()
It crashes here giving a 403 response code. The URL that is being referenced is an expired signature URL from Facebook. Firebase doesn't adjust to get the new appropriate URL, and it was from Firebase that I had been getting the URL. I can't figure out how to get it directly as tried below:
func getUrlOfMediumProfilePic(){
if (FBSDKAccessToken.current() != nil) {
let graphPathPart2 = "me/picture"
let paramsPart2 = ["type":"medium", "redirect":"false"]
let completionHandlerPart2 = { (connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
if let error = error {
print("Medium picture graph call contained an error: \(error.localizedDescription)")
return
} else {
guard connection != nil else {
print("getURLOfLargeProfilePic() function aborted bc connection failed.")
return
}
let results = result! as! NSDictionary
let dataDict = results["data"] as! NSDictionary
stringOfMediumProfilePicURLaka100x100 = dataDict["url"] as! String
print("medium picture graph call results define stringOfMediumProfilePicURLaka100x100 as: \(stringOfMediumProfilePicURLaka100x100)")
}
}
let graphRequest = FBSDKGraphRequest(graphPath: graphPathPart2, parameters: paramsPart2)!
graphRequest.start(completionHandler: completionHandlerPart2)
}else{
print("User not logged in when getURLOfMediumProfilePic() function was run.")
return
}
}
This code yields an error with code 8.
Have you tried this:
https://graph.facebook.com/{id}/picture?width=100&height=100
I don't know swift, so I can't help about syntax. I think you can make http request to url and get image.
Hope this help :)

Parsing data returned from server using Alamofire and SwiftyJSON, what is the data type returned?

I hope I manage to ask this properly:
I am using Alamofire and SwiftyJSON for managing the JSON files I get from the server.
I have issues with understanding the type of response.result.value , how to cast it to an object I can construct it with SwiftyJSON's JSON(data: data) constructor.
This is my code for the request using Alamofire:
func performRequest() {
// parameters["retry_count"] = retryNum
if let _ = host, let path = path {
let request = Alamofire.request(HOST + path, method: method, parameters: parameters, headers: headers)
request.responseJSON { response in
print("-----")
print(response.response?.statusCode)
print("-----")
// check if responseJSON already has an error
// e.g., no network connection
if let json = response.result.value {
print("--------")
print(json)
print("--------")
}
guard response.result.error == nil else {
print(response.result.error?.localizedDescription ?? "Response Error")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure we got JSON and it's a dictionary
guard let json = response.result.value as? [String: AnyObject] else {
print("didn't get dictionary object as JSON from API")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure status code is 200
guard response.response?.statusCode == 200 else {
// handle status code
self.completionHandler?(response.result.isSuccess, nil)
return
}
self.completionHandler?(response.result.isSuccess, json)
RequestsQueue.sharedInstance.sema.signal()
}
}
This results with this print:
{
numOfShiftsInDay = 3;
shifts = (
{
endTime = "14:00";
startTime = "07:30";
},
{
endTime = "20:00";
startTime = "13:30";
},
{
endTime = "02:00";
startTime = "19:30";
}
);
}
this data type is a [String: AnyObject].
I want to use it to construct a SwiftyJSON JSON object since it is easier for me to parse the data using SwiftyJSON methods..
This is the code I try for parsing it and then using it but obviously it doesn't work:
let json = JSON(data: data)
I get this compilation error:
Cannot convert value of type '[String : AnyObject]?' to expected argument type 'Data'
So how should I go about this?
You need to use JSON(data) instead of JSON(data: data) because this init(data:) wants Data as argument.
Changed line
let json = JSON(data: data)
To
let json = JSON(data)

Try! throwing fatal error in Swift 3, issues updating from Swift 2

I am trying to parse the JSON data from my server and I am getting an error when it hits the try! statement and it is crashing. It is telling me
Code=3840 "Invalid value around character 0.
It my be because I have not updated my code correctly to Swift 3. I was having an issue with if let parse for the longest time until I switched the as to as?
#IBAction func registerButtonTapped(_ sender: Any) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userRepeatPassword = repeatPasswordTextField.text;
// Check for empty fields
if((userEmail?.isEmpty)! || (userPassword?.isEmpty)! || (userRepeatPassword?.isEmpty)!){
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required");
return;
}
//Check if passwords matech
if(userPassword != userRepeatPassword){
// Display alert message
displayMyAlertMessage(userMessage: "Passwords do not match");
return;
}
// Send user data to server side
let myUrl = URL(string: "http://");
let request = NSMutableURLRequest(url:myUrl!);
request.httpMethod = "Post";
let postString = "email=\(userEmail)&password=\(userPassword)";
//adding the parameters to request body
request.httpBody = postString.data(using: String.Encoding.utf8);
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error=\(error)")
return
}
//parsing the reponse
//converting response to Any
let json = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as? [String:Any]
//parsing JSON
if let parseJSON = json{
let resultValue = parseJSON["status"] as? String
print("result: \resultValue)")
var isUserRegistered:Bool = false;
if(resultValue=="Success") { isUserRegistered = true;}
var messageToDisplay:String = parseJSON["messsage"] as! String;
if(!isUserRegistered)
{
messageToDisplay = parseJSON["message"] as! String;
}
DispatchQueue.main.async {
//Display alert message with confirmation.
let myAlert = UIAlertController(title:"Alert", message:messageToDisplay, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"ok", style:UIAlertActionStyle.default){ action in
self.dismiss(animated: true, completion:nil);
}
myAlert.addAction(okAction);
self.present(myAlert, animated:true, completion:nil);
};
}
}
task.resume()
}
Please help, thanks
The reason of the error is that you are sending literal "Optional(Foo)" strings to the server via String Interpolation. userEmail and userPassword will never match and the server sends no data back. In Swift 3 you have to explicitly unwrap even implicit unwrapped optional strings.
The solution is a waterproof error handling with optional bindings
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
guard let userEmail = userEmailTextField.text, !userEmail.isEmpty,
let userPassword = userPasswordTextField.text, !userPassword.isEmpty,
let userRepeatPassword = repeatPasswordTextField.text, !userRepeatPassword.isEmpty else {
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required")
return
}
...
Now all relevant optionals are safely unwrapped and the server will get the right data.
Further trailing semicolons and parentheses around if conditions are not needed in Swift and use URLRequest rather than NSMutableURLRequest in Swift 3
var request = URLRequest(url:myUrl!) // var is mandatory if properties are going to be changed.
PS: In any case – as already mentioned in the comments – never use carelessly try! when receiving data from a server.