Proper way to throw error from my closure? - ios

I'm actually trying to some code logic with swift for training purpose at the moment, I was wondering what is the proper way to throw my error from my init ?
So the flow is Controller ask for account creation when initializing Model is asking my sql manager to create the account and this method return the result from a closure.
But something feels wrong, should I just use a return from the sql manager who contained both my Int? and Error? ?
init(_ username: String, _ password: String) throws {
self.id = 0
self.username = username
self.password = password
var toThrow: Error? = nil
// Register in database
userManager.create(self) { (id: Int?, err: Error?) in
Thread.sleep(forTimeInterval: 10)
if let error = err {
// Register in database goes wrong
debugPrint("Handle error from user creation...")
toThrow = error
} else {
// There is no id and no error ?
guard let _ = id else { return }
self.id = id!
}
}
if let error = toThrow {
throw error
}
}

If you are on Swift 5 you could look into using Result and define your closure like
(id: Int) -> Result<Int, Error>
and change your code to
userManager.create(self) { (id: Int?) -> Result<Int, Error> in
Thread.sleep(forTimeInterval: 10)
if let error = err {
// Register in database goes wrong
debugPrint("Handle error from user creation...")
return .failure(error)
} else {
// There is no id and no error ?
guard let _ = id else { return }
return .success(id)
}
}
If you have your own error enum for the Db class like
enum DbError {
case create
case update
//...
}
Then you can use that type in the closure declaration
(id: Int?) -> Result<Int, DbError>
and return a specific error for this action
return .failure(.create)
Note that I haven't compiled this so consider it an example

Here is the solution if people want to see:
#IBAction func didPressRegister() {
guard let username = usernameField.text else { return }
guard let password = passwordField.text else { return }
let user = UserModel(username, password)
userManager.create(user) { result in
switch(result) {
case .failure(let error):
// TODO: UIAlert
debugPrint(error)
case .success(let int):
// TODO: Generate user token and redirect main
debugPrint(int)
}
}
}
// TODO
public func create(_ user: UserModel, _ complete: #escaping (Result<Int, Error>) -> ()) {
debugPrint("Requested to create the user... \(user)")
complete(.failure(toThrow.ACCOUNT_ERROR))
}

Related

AWS Cognito Import throw userNotFound

I'm trying to migrate a batch of users into Cognito via a CSV (i.e. one-time import). This part works successfully and I can see the users in the User Pool. As expected, I see the user with a status of "Enabled / RESET_REQUIRED". So far so good, right?
When I try to sign-in to the user in my mobile app, it throws an error of "userNotFound" instead of the "passwordResetRequired" which would trigger the proper flow.
Has anyone seen this? Or know how to solve this problem?
Just for grins, here is my code:
private func signInWithAWS(email: String, password: String) -> Observable<AWSSignInResult> {
Observable<AWSSignInResult>.create { observer in
AWSMobileClient.default().signIn(
username: email.lowercased(),
password: password,
completionHandler: { signInResult, error in
if let signInResult = signInResult {
switch signInResult.signInState {
case .signedIn:
observer.onNext(.success)
observer.onCompleted()
default:
observer.onCompleted()
}
} else if let error = error as? AWSMobileClientError {
if case AWSMobileClientError.userNotConfirmed = error {
observer.onNext(.unconfirmed)
observer.onCompleted()
return
} else if case AWSMobileClientError.passwordResetRequired = error {
observer.onNext(.resetPasswordRequired)
observer.onCompleted()
}
let message = getCognitoErrorMessage(error: error)
observer.onError(AuthServiceError.signIn(message))
}
}
)
return Disposables.create()
}
}
It fails the "userNotConfirmed" and "passwordResetRequired" checks into the generic handler because the actual error is "userNotFound".

Code after guard is called later than expected

Hi I am new in iOS development and I am having hard time to understand the following issue. Basically I am trying to get user's name by passing current user's id to Cloud Firestore. However I am having hard time to understand a bug in the code. I can successfully pass the name of user to name variable, while the function returns default value of name which is "" (empty string). It seems that the block of code inside
if let data = snapshot?.data() {
guard let userName = data["name"] as? String else { return }
name = userName
print("after guard") // this line
}
happens later than
print("name") // this line
return name
Full code:
private func returnCurrentUserName() -> String {
// User is signed in.
var name = ""
if let user = Auth.auth().currentUser {
let db = Firestore.firestore()
db.collection("users").document(user.uid).getDocument { (snapshot, error) in
if error == nil {
if let data = snapshot?.data() {
guard let userName = data["name"] as? String else { return }
name = userName
print("after guard") // this line
}
}
}
print("name") // this line
return name
}else {
return ""
}
}
(Note: the query from Cloud Firestore is successful and I can get users name on the console but "name" is printed after "after guard".)
In addition to the other answer:
If you would like to execute code after your operation is done, you could use a completion block (that's just a closure which gets called upon completion):
private func returnCurrentUserName(completion: #escaping () -> ()) -> String {
// User is signed in.
var name = ""
if let user = Auth.auth().currentUser {
let db = Firestore.firestore()
db.collection("users").document(user.uid).getDocument { (snapshot, error) in
if error == nil {
if let data = snapshot?.data() {
guard let userName = data["name"] as? String else { return }
name = userName
completion()//Here you call the closure
print("after guard") // this line
}
}
}
print("name") // this line
return name
}else {
return ""
}
}
How you would call returnCurrentUserName:
returnCurrentUserName {
print("runs after the operation is done")
}
Simplified example:
func returnCurrentUserName(completion: #escaping () -> ()) -> String {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
completion() //runs after 4 seconds
}
return "xyz"
}
let test = returnCurrentUserName {
print("runs after the operation is done")
}
print(test)
The reason is your getDocument is an asynchronous operation. It takes a callback, and that callback will be invoked when the operation is done. Because of the asynchronous operation, the program will continue process the next line without waiting for the async operation to be completed. That's why you see your print("name") getting executed before the print("after guard")

PromiseKit - Pass previous result to next promise in chain

Im trying to follow the Documentation to pass the result of a promise to the next promise
https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#saving-previous-results
I keep getting
Contextual closure type '(_) throws -> _' expects 1 argument, but 2 were used in closure body
Here my Code
class func loginInOrSignup() -> Promise<PFUser> {
let lm = LoginManager()
lm.logOut()
return logInInBackground().then{user in
if user.isNew{
return getUserInfo().map{ ($0, user) }
}
else{
user.fetchIfNeededInBackground()
return Promise.value(user)
}
}.then{(userInfo, user) -> Promise<PFUser> in
let name = userInfo["name"] as! String
if let email = userInfo["email"] as? String {
user.email = email
}
let username = G8FacebookLogin.generateSuggestedUsername(name: name)
user.username = username
return Promise.value(user)
}
}
private class func logInInBackground() -> Promise<PFUser>{
return Promise {seal in
PFFacebookUtils.logInInBackground(withReadPermissions: ["public_profile", "email"]){user, error in
guard let user = user else {
seal.reject(error ?? AuthError.msg(reason: "Some FB Error"))
return
}
seal.fulfill(user)
}
}
}
private class func getUserInfo() -> Promise<Dictionary<String,Any>> {
return Promise {seal in
G8FacebookLogin.getUserInfo { (userDict, error) in
guard let userInfo = userDict else{
PFUser.logOut()
seal.reject(AuthError.loginFailed(reason: "no user Info retrieved"))
return
}
seal.fulfill(userInfo)
}
}
}
It's pretty much the same as the Snippet code in the documentation. I don't understand what is the right way to do it.

Reassigning a value in an if statement in Swift [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 3 years ago.
the following is printing "no user with username" but is printing retVal as "false" ( I changed function to a string just for troubleshooting, ideally this should be bool ) I am new to swift and this is absolutely driving me crazy. it is making it to the chunk of code where retVal would get reassigned, but it isn't reassigning it
static func isUserNameUnique(_ username : String) -> String {
var retVal = "false"
let db = Firestore.firestore()
let newQuery = db.collection("users").whereField("userName", isEqualTo: username)
newQuery.getDocuments { (document, error) in
if document!.isEmpty {
retVal = "true"
print("No user with username")
}
}
print("\(retVal)")
return retVal
}
func validateFields() -> String? {
//Check that all fields are filled in
if premierCodeTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" || userNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all fields."
}
//Check unique username
let cleanedUserName = userNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
Utilities.isUserNameUnique(cleanedUserName) { res in
if !res {
// return "please choose a unique username"
}
}
return nil
}
You are trying to return a value synchronously while using and asynchronous method.
When you do newQuery.getDocuments execution continues without waiting for completion handler to be called. So after that line is executed, the return is executed, and THEN the completion handler gets called.
If you want to get a value from an asynchronous method, you need to create a method that takes a completion handler like the answer Khan gave you.
static func isUserNameUnique(_ username: String, completionHandler: #escaping (Bool) -> Void) {
let db = Firestore.firestore()
let newQuery = db.collection("users").whereField("userName", isEqualTo: username)
newQuery.getDocuments { (document, error) in
completionHandler(document!.isEmpty)
}
}
You need a completion as the request is asynchnous , plus use Bool instead of a String
static func isUserNameUnique(_ username : String,completion:#escaping((Bool) ->())) {
let db = Firestore.firestore()
let newQuery = db.collection("users").whereField("userName", isEqualTo: username)
newQuery.getDocuments { (document, error) in
completion(document!.isEmpty)
}
}
Call
Utilities.isUserNameUnique { res in
if !res {
// name exists
}
}
It's impossible to achieve what you want since newQuery.getDocuments isn't returning value instantly. It will answer you at some point by calling function that you passed to it.
Your code can be described as
func foo() -> String {
// set retVal to "false"
var retVal = "false"
// create query
let db = Firestore.firestore()
let newQuery = db.collection("users").whereField("userName", isEqualTo: username)
// ask query to evaluate
newQuery.getDocuments { (document, error) in
// at some point probably after foo ends
if document!.isEmpty {
// if document is not empty set retVal to "true" (at this point there is no-one that could look at value of retVal)
retVal = "true"
print("No user with username")
}
}
// while query is evaluating in background
// print retVal (probably still "false")
print("\(retVal)")
// return retVal (probably still "false")
return retVal
}
Now let's fix your problem.
Solution could be:
class X {
private var document: <insert correct type here>? {
didSet {
// do what you want with document
}
}
func foo() {
let db = Firestore.firestore()
let newQuery = db.collection("users").whereField("userName", isEqualTo: username)
newQuery.getDocuments {
[weak self] (document, error) in // [weak self] is important!
// I have no idea on which thread firebase runs it's callback
// It's important that only one thread could modify self.document
// otherwise you will have race condition and a lot of strange behaviours
DispatchQueue.main.async {
self?.document = document;
}
}
}
}
If you really need to create func foo() -> String and you don't care that your thread will have to wait (UI will not respond, you will have 0 fps etc) you can do it using NSLock (I won't post code since it's really bad idea in most of the cases).

How to create an abstract function to reduce code duplication

I have a few functions that are basically the same apart from a few variable names that they reference. I want to abstract the function so that I don't have to keep duplicating the code. This is an example function:
func listenToParticipantNumber() {
guard let reference = participantNumberReference else {
return
}
guard participantNumberListener == nil else {
return
}
participantNumberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
guard let strongSelf = self else {
return
}
switch result {
case .success(let participantNumber):
strongSelf.participantNumber = participantNumber
case .failure:
break
}
}
}
In another function, I'd switch out participantNumberReference, participantNumber, and participantNumberListener for different variables (which are all private to my class), and the block return type of Int. But the core layout of the function would be the same.
How can I make this process cleaner to reuse this code rather than having to duplicate it? Is it possible to somehow use KeyPaths to reference different variables of my class?
It takes a lot of pre-amble to achieve this abstraction level, so you have to ask larger questions about What You're Really Trying To Do, and Is It Worth It, but here's an outline. The essence is to use a dictionary with properties instead of explicitly declared variables.
Now you can add future cases to PersonType and initialize them (which may be automate-able) and the function will apply to all of them regardless. PersonType could conceivably be declared outside the class to increase separation.
This is probably a better suited for Code Review, where you can post a lot more context.
enum PersonType { case Participant, case Leader, case Observer }
struct TrackedObject { var number: Int; var numberReference: ReferenceProtocol; var numberListener: ListenerProtocol; }
// Instead of 9 private variables, make a Dictionary of 3 objects each of
// which have 3 properties
private var persons: [ PersonType: TrackedObject ] = ...
func listenToPersonOfType(personType: PersonType) {
guard let reference = persons[personType].numberReference else {
return
}
guard persons[personType].numberListener == nil else {
return
}
persons[personType].numberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
guard let strongSelf = self else {
return
}
switch result {
case .success(let number):
strongSelf.persons[personType].number = number
case .failure:
break
}
}
}
I think you're on the right road with a KeyPath. I would probably refactor this a little bit so that the listen method returns Listener? (whatever type that is), and hoist the "if listener is already set, don't do this," outside of this function.
That would leave you something along these lines:
func listen<Response>(reference: Int?, // Or whatever type this is
updating keyPath: WritableKeyPath<C, Response>,
completion: (Result<Response, Error>) -> Void) -> Listener? {
guard let reference = reference else { return nil }
return backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
Or you can keep your existing structure with something like this:
func listen<Response>(reference: Int?,
updating keyPath: WritableKeyPath<C, Response>,
forListener listener: WritableKeyPath<C, Listener?>,
completion: (Result<Response, Error>) -> Void) {
guard let reference = reference else { return }
guard self[keyPath: listener] == nil else {
return
}
var mutableSelf = self
mutableSelf[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
}
(C in all of this code is "this class." We can't yet write Self to mean "the current class.")
So I solved it like this:
private func listen<T>(
to property: ReferenceWritableKeyPath<LivestreamModel, T?>,
withReference propertyReference: ReferenceWritableKeyPath<LivestreamModel, String?>,
listener: ReferenceWritableKeyPath<LivestreamModel, DatabaseHandle?>) {
guard let reference = self[keyPath: propertyReference] else {
return
}
guard self[keyPath: listener] == nil else {
return
}
self[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<T, RequestError>) in
switch result {
case .success(let value):
self?[keyPath: property] = value
case .failure:
self?[keyPath: property] = nil
}
}
}
I actually did this before Rob replied, but I had to use ReferenceWritableKeyPath because it gives a warning that self has no subscript members when using WritableKeyPath.

Resources