Why is the firebase security rules not working? - ios

I'm using RealTime DataBase for my application, it has security rules:
{
"rules": {
".read": "auth.uid != null",
".write": "auth.uid != null"
}
}
This is the code snippet where the data is getting:
func firstLaunchCataloguePartsFetchData(
result : #escaping((Result<[CatalogueParts], Error>) -> Void)
) {
if ConnectionManager.shared.isConnected {
let baseUrl = Constants.baseUrl + DirectoryType.catalogueParts.addJsonAbbreviation
guard let url = URL(string: baseUrl ) else { return }
URLSession.shared.dataTask(with: url) {data, response, error in
if let error = error {
result(.failure(error))
} else if let data = data,
let jsonString = String(data: data, encoding: .utf8),
let downloadedMarks = Mapper<CatalogueParts>().mapArray(JSONString: jsonString) {
result(.success(downloadedMarks))
} else {
result(.success([]))
}
}
.resume()
} else {
let error = NSError(domain: "Connection error detected", code: 911, userInfo: nil)
result(.failure(error))
}
}
Here I am using the received data to draw a table view:
func getFirstLaunchCatalogueParts() {
ShopManager.shared.firstLaunchCataloguePartsFetchData { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let catalogueParts):
self.catalogueParts = catalogueParts
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .failure(let error as NSError):
self.showErrorAlert(error: error, withAction: nil)
}
}
}
Even if I'm authorized, passed phone authorization (Auth.auth().currentUser?.uid has somevalue) - i can't display the tableView, received data is nil
But!
This works great, the table is rendered, everything works if the rules are made like this:
{
"rules": {
".read": "true",
".write": "auth.uid != null"
}
}

In my opinion the problem was this:
I was accessing Firebase using this method, it is not the basic method of reading firebase service data
let baseUrl = Constants.baseUrl + ".JSON"
// where baseUrl is just http path to my database and if you add ".JSON" to it - you
// open the contents of the database in Json
guard let url = URL(string: baseUrl ) else { return }
URLSession.shared.dataTask(with: url) {data, response, error in
Then I used the given Data to parse through the model:
let jsonString = String(data: data, encoding: .utf8),
let downloadedDataAsModel = Mapper<CatalogueParts>().mapArray(JSONString: jsonString)
The problem is that using this method, regardless of the rules - you get the data in any case, while the ObjectMapper processes and creates an instance, but it is incomplete, and no error occurs here
let downloadedDataAsModel = Mapper<CatalogueParts>().mapArray (JSONString: jsonString)
Therefore, this method does not allow rule settings other than "read : true"
I tried using this method and everything really worked - depending on the authorized status, access was allowed or restricted. Now it remains to decide how to use this method to get data in the form of Json for further processing =)
func fetchData(){
let ref = Constants.ref
ref.child("CatalogueParts").getData { error, snapshot in
guard error == nil else {
print(error!.localizedDescription)
return;
}
let someValue = snapshot.value
print(someValue)
}
}

Related

Swift 4 Properly Parse JSON String to Object

I'm trying to parse a JSON string coming from a URLSession request into a Swift object.
I managed to get the data for the first level properties but for nested properties something weird happens. Instead of : i get = AND strings are missing the double-quotes
How do I access the date property inside published because I can not do this: print(todo["published"]["date"])
Here is the data I get:
[
"pretty_artists": kida,
"published": {
date = "2015-12-05";
now = 1517005961;
time = "18.59";
timestamp = 1449341947;
},
"views": 36,
"yt_id": cyXbV7EUl14,
"play_start": 0,
"title": ski ide,
"duration": 235,
"video_name": skiide,
"artists": kida
]
Here is my function:
func makeGetCall(todoEndpoint: String) {
// Set up the URL request
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = 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: responseData, options: [])
as? [String: Any] 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(todo["published"]["date"])
// 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
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
try the SwiftyJSON library, it should help you parse the data more easily.
https://github.com/SwiftyJSON/SwiftyJSON

swift JSON login REST with post and get response example

It's my first experience with REST in iOS development with swift. I couldn't find any working or straight (simple) example for doing what i need here.
I have a login backend (https://myaddress.com/rest/login), where I need to pass 2 params: login and password. When I pass good values (user exists in database) I get 2 variables as a result: token (string) and firstLogin (bool). So when I get those values I know that login is successful and I can log in into my app.
So I am begging you for an example (just a simple function) of how to achieve that. If I get working code example I will know how to use it for other rest services in my app. I tried many solutions from tutorials I found, but any of them was working for me.. So to not waste my time searching I would like someone experienced to show me the way to achieve that.
I am not sure if Alamofire is so good to use, I know that swift 4 has it's own build neetwork services and to work with json. Any solution that works would be great.
Also, side question - if I would prefer to use Alamofire, do I need to use swiftyJSON also? Or it's just for parsing?
You can use URLSession if you don't like to import Alamofire in your Project to perform a simple task.
here are some method : GET, POST, DELETE METHODS and tutorial
GET METHOD
func makeGetCall() {
// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = 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: responseData, options: [])
as? [String: Any] 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.description)
// 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
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
POST METHOD
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "My First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,
options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
DELETE METHOD
func makeDeleteCall() {
let firstTodoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
var firstTodoUrlRequest = URLRequest(url: URL(string: firstTodoEndpoint)!)
firstTodoUrlRequest.httpMethod = "DELETE"
let session = URLSession.shared
let task = session.dataTask(with: firstTodoUrlRequest) {
(data, response, error) in
guard let _ = data else {
print("error calling DELETE on /todos/1")
return
}
print("DELETE ok")
}
task.resume()
}
Thanks #MAhipal Singh for you answer. I'll post here example with Alamafire that I used so it's all in one stack question. It's easier than I though, solutions I tried to use before were not working cause I had problems with pinning certificate about I forgot..
func loginRest(login:String, password:String, deviceId:String){
let urlStr = restServices.REST_MAIN_URL + restServices.REST_LOGIN
let params = ["login":login, "password":password, "deviceId":deviceId]
let paramsJson = try! JSONSerialization.data(withJSONObject: params)
var headers: HTTPHeaders = ["Content-Type": "application/json"]
Alamofire.request(urlStr, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
print("SUKCES with \(response)")
case .failure(let error):
print("ERROR with '\(error)")
}
}
If the post is proper the response is (console print):
SUKCES with SUCCESS: {
firstLogin = 1;
token = "dfkafjkfdsakfadsjfksjkfaadjfkjdfkjfskjfdkafjakfjakfjsafksjdafjy878328hjh";
}

how to wait for the URLSession to finish before returning the result from a function in Swift 3

Hi i have a beginner question, where i cant find a good solution for in Swift 3. I hope someone is able to hlep.
i have the following code that will check a rest api if the user credentials are valid or not. I want it to wait for the resquest is finished and then return true or false. now it is being send async.
Also any improvement in the way i check the JSON value's would be welcome too.
func CheckUsernamePassword(username :String ,code:String )-> bool {
var validCredentials = false
let urlString = "\(self.baseurl)/accounts/validateusernamepassword.json?username=\(username)&password=\(code)&api_key=\(self.api_key)"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print("Error URLSession : \(error!)")
validCredentials = false
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
if parsedData["validated"] != nil {
if "\(parsedData["validated"]!)" == "1" {
print("Login credentials are correct")
validCredentials = true
}else {
print("Login credentials are not correct")
print("\(parsedData["validated"]!)")
print("\(parsedData["message"]!)")
validCredentials = false
}
}else{
print("Json Parse error: \(parsedData)")
validCredentials = false
}
} catch let error as NSError {
print("Error Parsing Json \(error)" )
validCredentials = false
}
}
}.resume()
return validCredentials
}
You cannot return something from an asynchronous task as a return value.
Do not wait, use a completion handler:
Replace the signature of the method (the name is supposed to start with a lowercase letter) with
func checkUsernamePassword(username: String, code: String, completion: #escaping (Bool)->() ) {
Delete the lines var validCredentials = false and return validCredentials
Replace all occurrences of validCredentials = false with completion(false) and
validCredentials = true with completion(true).
Call the method
checkUsernamePassword(username: "Foo", code: "Baz") { isValid in
print(isValid)
// do something with the returned Bool
DispatchQueue.main.async {
// update UI
}
}

Xcode: URL Shared Session not running when unit testing

I have made a simple app which adds data to a database, and then retrieves it. Whilst creating unit testing, it appears that the URLSession.Shared.dataTask is not running. I can see this through the output of the print statements I have setup. Below is my code:
func addChild(childName:String,dob:String,number1:String,number2:String,parentNum:String,parentPass:String,notes:String){
//url to php file
let url = NSURL(string:"http://localhost/addChild.php")
//request to this file
let request = NSMutableURLRequest(url: url as! URL)
//method to pass data to this file
request.httpMethod = "POST"
//body to be appended to url
let body = "childName=\(childName)&dateOfBirth=\(dob)&contact1=\(number1)&contact2=\(number2)&parentAccNum=\(parentNum)&parentAccPass=\(parentPass)&notes=\(notes)"
request.httpBody = body.data(using: String.Encoding.utf8)
print("a")
//launching the request
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in
print("b")
if (error == nil){
print("c")
//send request
//get main queue in code process to communicate back to user interface
DispatchQueue.main.async(execute: {
do{
//get json result
let json = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
print("d")
//assigning json to parseJSON in guard/secure way
//checking whether the parsing has worked
guard let parseJSON = json else{
print("Error while parsing")
return
}
//get id from parseJSON dictionary
let id = parseJSON["id"]
//if there is some id value
if id != nil{
print(parseJSON)
self.success = true
print("success")
}
}
catch{
print("Caught an error:\(error)")
}
} )
}
//if unable to proceed request
else{
print("Error:\(error)")
}
//launch prepared session
}).resume()
}
And then below is my unit testing script:
import XCTest
#testable import computerScienceCoursework
class addChildTest: XCTestCase {
//Setting up the values of the text fields
var testChildName:String = "Test name"
var testDOB:String = "99/99/99"
var testContact1:String = "00000000000"
var testContact2:String = "11111111111"
var testParAccNum:String = "-1"
var testParAccPass:String = "Password"
var testNotes:String = "Insert notes here"
var newChild = AddChildController()
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testAddChildIsWorking(){
//Assigning the values to the text fields
newChild.addChild(childName: testChildName,dob: testDOB,number1: testContact1,number2: testContact2,parentNum: testParAccNum,parentPass: testParAccPass,notes: testNotes)
XCTAssert(newChild.success == true)
}
}
Problem here is that you donĀ“t know when the async task is finished and the success property is getting updated.
There are some possible solutions for your problem one of them is to add a completion handler to your method.
func addChild(childName:String,dob:String,number1:String,number2:String,parentNum:String,parentPass:String,notes:String, completion: (Bool) -> Void){
//url to php file
let url = NSURL(string:"http://localhost/addChild.php")
//request to this file
let request = NSMutableURLRequest(url: url as! URL)
//method to pass data to this file
request.httpMethod = "POST"
//body to be appended to url
let body = "childName=\(childName)&dateOfBirth=\(dob)&contact1=\(number1)&contact2=\(number2)&parentAccNum=\(parentNum)&parentAccPass=\(parentPass)&notes=\(notes)"
request.httpBody = body.data(using: String.Encoding.utf8)
print("a")
//launching the request
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in
print("b")
if (error == nil){
print("c")
//send request
//get main queue in code process to communicate back to user interface
DispatchQueue.main.async(execute: {
do{
//get json result
let json = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
print("d")
//assigning json to parseJSON in guard/secure way
//checking whether the parsing has worked
guard let parseJSON = json else{
print("Error while parsing")
completion(false)
return
}
//get id from parseJSON dictionary
let id = parseJSON["id"]
//if there is some id value
if id != nil{
print(parseJSON)
self.success = true
print("success")
completion(true)
}
}
catch{
print("Caught an error:\(error)")
completion(false)
}
} )
}
//if unable to proceed request
else{
print("Error:\(error)")
completion(false)
}
//launch prepared session
}).resume()
}
Then in your test method you can the method.
func testAddChildIsWorking()
{
let asyncExpectation = expectationWithDescription("addChildIsWorkingFunction")
newChild.addChild(childName: testChildName, dob: testDOB, number1: testContact1,
number2: testContact2, parentNum: testParAccNum, parentPass: testParAccPass, notes: testNotes) { (success) in
asyncExpectation.fulfill()
}
self.waitForExpectationsWithTimeout(10) { error in
XCTAssert(newChild.success == true)
}
}
waitForExpectationWithTimeout is waiting until a fulfill is trigger or a timeout occurs. In this way you could test your async code.
For more informations check this link
Hope that helps.

Missing return in a function expected to return 'NSURLSessionDataTask'

I am following a tutorial on accessing an api and parsing the result. I am following the tutorial word for word but I cannot run the program because of 'Missing return in a function expected to return 'NSURLSessionDataTask'
so I changed the return statement to "return NSURLSessionDataTask" but then got an error saying "Cannot convert return expression of type 'NSURLSessionDataTask.Type" to return type 'NSURLSessionDataTask'
How do i figure out the return type? do I even need a return? because in the tutorial there is not return statement (i tried without return as well).
func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void)
-> NSURLSessionDataTask {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}
Did you really mean to write your own method called dataTaskWithRequest which looks just like the NSURLSession method of the same name? The problem is that you said you're writing a method that returns a NSURLSessionTask object, but you don't return anything.
I'd think you meant something like the following, renaming your own method to something else, and specifying that it's not returning anything itself, because it's not:
func performRequest() {
let postEndpoint: String = "http://jsonplaceholder.typicode.com/posts/1"
let urlRequest = NSURLRequest(URL: NSURL(string: postEndpoint)!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not recieve data")
return
}
guard error == nil else {
print("error calling GET on /posts/1")
print(error)
return
}
// parse the resutl as JSON, since that's what the API provieds
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData, options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
// now we have the post, let's just print it to prove we can access it
print("The post is: " + post.description)
if let postTitle = post["title"] as? String {
print("The title is: " + postTitle)
}
})
// and send it
task.resume()
}

Resources