I have this use case where a model object (e.g. class User) has few methods.
Some of the methods in the class require authentication (e.g. getProfile, getFriends,...).
class User{
var loginDelegate:LoginDelegate
func getProfile{
HTTPAsync.getProfile(payload){response in
if response.status == 401 {
login(delegate)
}
}
func getFriends{
//similar code as above
login(delegate)
}
Once, user is successfully logged in, I want to call respective functions (getFriends, getProfile, whichever invoked login).
I have been thinking to use delegate pattern. But since my class (user) has multiple methods that require login, I need to pass some data to delegate, which must be read after user is logged in to call the appropriate method.
I am new to Swift, and was wondering if I am going in the right path. Is there any other obvious way to achieve this pretty common problem.
In my app, use a Url whiteList to solve this problem,
For example, the Url inside the user authentication interface which contains "/users/" this string (or other strings), when the user is not logged in and used a request for such a Url to send out a notification, by a unified class to receive this notification,then Pop up Login box
I am new to Swift, and was wondering if I am going in the right path. Is there any other obvious way to achieve this pretty common problem.
Yes there are a couple of ways you might choose to solve this. e
Define getter methods on your delegate protocol, if it is not your own delegate protocol you can use an extension to extend it's functionality.
Create an Enumeration as an instance variable so you can set an enumeration value with in the login method that your other methods can access after the login method finishes.
Change the login method to accept more parameters and returns a value\object.
For example:
login(delegate: LoginDelegate, dictionaryOfOtherStuff: [String :AnyObject]?) -> (value_1: String, value_2 : [int])
I can only give an example since you have not stated exactly what needs to be available after the login method is called.
Related
I was playing around with Firebase Auth and I noticed all FirebaseAuth.User instances have a method called setValue(value:forKey:). I searched around in the "Manage Users" section of the Firebase Auth Docs but found no reference of it.
I wanted to know what it does. I'm guessing you can use it to set some special value (somewhat like a property) to a FirebaseAuth.User. If that's the case would it be useful for storing additional data associated with a user such as "preferredGenres" or "emergencyNumber"?
Please let me know what setValue(value:forKey:) does, what are its uses, and if my assumption is correct.
Thanks in advance!
As you can see from User class declaration:
class User : NSObject, UserInfo it inherited from an NSObject which has setValue(value:forKey:) method. Within NSObject this method allows to set any properties via String key. Apple docs are here: setValue(forKey:)
So in common you can just access and mutate all the existing User properties but in a less safe manner.
I have an unit test which calls methods on CNContactStore() e.g. CNContactStore().execute(saveRequest). So the permission dialog for contacts pops up, like the Push notifications alert but the contacts permission dialog doesn't get dismissed automatically. I know how to do this in UI tests with addUIInterruptionMonitor() but have no idea how to do this in unit test.
I would create a wrapper around CNContactStore and then use a mock when testing.
You're not really interested in testing CNContactStore, you are interested in testing that your code interacts with CNContactStore properly right?
Setup
I would start out creating protocols and classes to extract the contact stuff out of your "normal" code base.
First a Contact struct to hold the properties you need later to create an actual CNContact
struct Contact {
//holds whichever properties you need to create a CNContact
}
Then a protocol to hold the methods you would like to execute. This could be done with a protocol with a lot of methods like so
protocol ContactsHolder {
func save(contact: Contact)
func add(contact: Contact)
func delete(contact: Contact)
func update(contact: Contact)
//Maybe more methods, the important thing is that you abstract yourself away from CNContactStore and other Contact kit classes
}
Or you could create an enum holding the possible options like so
enum ContactsUpdateMethod {
case save(Contact)
case add(Contact)
case delete(Contact)
case update(Contact)
}
protocol ContactsHolder {
func execute(_ method: ContactsUpdateMethod)
}
In Your "Real" Code
With that in place, you are ready to create your actual ContactsHolder, which then internally uses CNContactStore and everything related to that framework.
So for instance (if you chose the version with a "pure" save function)
class CNContactsHolder: ContactsHolder {
func save(contact: Contact) {
//1. create a `CNContact` from your `Contact`
//2. create a saveRequest
//3. execute: CNContactStore().execute(saveRequest)
}
....
}
And then you give the class(es) who needs to work with CNContactStore a reference to your new ContactsHolder protocol
So in your class you have
let contactsHolder: ContactsHolder
And then you can either pass it in, in your init method
init(contactsHolder: ContactsHolder = CNContactsHolder()) {
self.contactsHolder = contactsHolder
}
Or you can declare it as a var and then give it a default value
So instead of:
let contactsHolder: ContactsHolder
You say:
var contactsHolder: ContactsHolder = CNContactsHolder()
The important thing is that you can change the ContactsHolder from being a "real" CNContactsHolder into a mock when you need to test
In Your Test Code
To test this, you create a mock:
struct MockContactsHolder: ContactsHolder {
var saveWasCalled = false
func save(contact: Contact) {
saveWasCalled = true
}
}
And then you use that in your class instead of the CNContactsHolder
Now you should be able to test your own code, without getting interrupted with permissions and stuff that is not relevant to your code, but is a consequence of using CNContactStore.
Disclaimer :)
I haven't run the above by a compiler, so there may be typos.
Also, there might be bits and pieces missing to make it fit to CNContact (callbacks and so on), but I hope you get the idea about how to split things apart.
And finally...it may seem like a lot of work, but I think it makes sense to get the "framework specific" code out into a separate helper class, hid behind a protocol, so that you can swap it out whenever you need to do testing for instance, or...if you decide to get rid of CNContact at a later point and use a different frameworks.
Hope it helps.
I think you're confusing Unit Testing with UI Testing. In Unit Testing, you just want to test, your codes (e.g. functions and properties) and with that, you'll most probably need to have "mock-up".
For instance, you want to test your login button selector that has a network calls after validation of the input fields.
The following should be the steps:
Test your validation logic. Both failing and succeeding cases.
Test the code inside the completion block of your API call, BUT not using the REAL API data. Instead, use your mocked API here.
and so on...
Now, back to your question, you don't need to handle that uncontrollable and "un-dismissable" alert controller generated by the system. Instead, what you wanna do is to "mock" (ughh not again) that pop-up event by hitting the delegate function for that access-contacts alert by the system, "mock" a response namely "Don't Allow" and "OK". What do you expect to happen when user taps on the first button? The second button? Set expectations/assert.
That's it. Hit every function you need to hit to increase the coverage of your code. Let me know if this helps.
The question (reflection or something like it?)
Is it possible (in Swift) to extract all the method signatures of an iOS protocol programmatically, such as for UITextViewDelegate, whose methods are all optional, without having to instantiate a separate class that explicitly implements all the protocol's methods?
In this case, want to intervene as the delegate to intercept one of the methods and do some related operation to that activity, then daisy chain the delegate call forward. But unfortunately, becoming the delegate entails responsibility to forward all of the protocol the downstream consumer
If you're saying what I think you're saying, there actually is a very simple way to do this: implement forwardingTarget(for:), as I do here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch12p611tabBarMore/ch25p882tabBarMore/MyDataSource.swift
The idea is that I don't know what the table view's dataSource actually does, because the table view and its data source belong to Cocoa, but I want to act as a "man in the middle" between them and just tweak the behavior of one data source method if it is called. All other method calls just get passed along, without my even knowing what they are.
I am making an app for iOS in Swift and I am trying to keep my API call logic outside of my ViewControllers. I followed this tutorial for 1 API call that I'll describe below.
I am creating a protocol 'APIControllerProtocol' that the ViewController will implement, and then I am creating an APIController class that will contain a property called 'delegate' to hold an instance of type APIControllerProtocol. The protocol is simply defined as
protocol APIControllerProtocol {
func didReceiveAPIResults(result: SwiftyJSON.JSON)
}
And the ViewController will implement the didReceiveAPIResults function to be called after the API call returns JSON data. The ViewController will contain an instance of the APIController as well to call a function which makes the call to the API.
At this point I want to have more ViewControllers perform API calls, and if I only need to make 1 API call per ViewController, I can just add another function to APIController, and I can have the new ViewController use the same protocol. However, this logic is flawed if there is a ViewController that would need to make 2 or more API calls, because
I cannot call didReceiveAPIResults in the ViewController in 2 different ways,
I don't think adding multiple 'delegate' properties to the APIController class to hold different types of protocols is the way to go.
Defining multiple functions in a protocol would mean I would have to implement all of these functions in ViewControllers that only need to make 1 API call.
My ideas to solve this problem are
Make an APIController class and APIControllerProtocol for each ViewController
Use optional functions in the APIControllerProtocol so not every ViewController would have to implement all of the functions (I don't know how this would really work)
Any other insights would be appreciated.
Your use of the term "API" is confusing. It sounds like what you mean is a server request, so that's what I'm going to call it.
I would suggest using a completion block/closure design rather than a protocol-based design.
Make your calls to your network request class take a completion closure, and call that completion closure once the server request is done.
You can write your network request class to maintain a list of requests and the completion closure for each pending request, and invoke the closure for each request once it completes or fails.
Defining multiple functions in a protocol would mean I would have to
implement all of these functions in ViewControllers that only need to
make 1 API call.
This is incorrect. Create optional functions and you probably have your answer.
#objc protocol APIControllerProtocol {
func didReceiveAPIResults(result: SwiftyJSON.JSON)
optional func someAwesomeMethod()
}
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
Optional Protocol Requirements
You can define optional requirements for protocols, These requirements
do not have to be implemented by types that conform to the protocol.
Optional requirements are prefixed by the optional modifier as part of
the protocol’s definition.
An optional protocol requirement can be called with optional chaining,
to account for the possibility that the requirement was not
implemented by a type that conforms to the protocol. For information
on optional chaining, see Optional Chaining.
You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.
The tutorial you're basing your logic on does not look ok to me. What it does, it implements a standard delegation pattern tweaked to the max. DetailsViewController has an APIViewController instance to which it assigns itself as the delegate - pretty standard. But then DetailsViewController calls methods on that APIViewController instance in order to get the delegate methods fired, which are implemented in DetailsViewController. That's too much going in circles for my taste.
A much more sensible pattern would be to create a singleton (shared instance) object which would handle your API calls and any view controller could access it. There you can implement your methods that take your parameters, one of them being a closure which could be called when your request completes passing the result to the view controller.
So in your view controller you'd call something like this:
APIController.sharedInstance.doSomethingWith(someParameter, completion: { (result) -> Void in
// Do something with the result
})
Your APIController would implement this something like this
func doSomethingWith(param: String, completion: (AnyObject) -> Void) {
// do your data fetching here...
// pass the result to the closure when the data is retrieved
completion(result)
}
So in my app I have the following situation:
BackendCommunicatingClass -> (owned by) -> ModelClass -> (owned by) -> HomescreenViewController
HomescreenViewController is a delegate for ModelClass.
ModelClass is a delegate for BackendCommunicatingClass.
Also on when the app launches for the first time, I have this:
WelcomeViewController -> (owned by) -> HomescreenViewController
HomescreenViewController is delegate for WelcomeViewController.
When the user types a username and password in the WelcomeViewController, this information needs to get all the way to BackendCommunicatingClass, and then all the way back to WelcomeViewController to display error if needed. Right now I have implemented this by passing information to each class in the communication chain, until it gets to BackendCommunicatingClass. The result is a lot of duplication of protocol methods and I feel like I'm doing it wrong.
What do you think?
Well I understand it is not the clearest solution, but without seing the code, and the purpose of your program, this what I suggest.
Implement Key Value Observing (KVO) in Back End view controller, observing change in the Home View Controller. As soon as Back end controller detect change in the text field, trough a dedicated variable in Home View controller, it fires all the operation it has to do.
When back end finish, it sends a NSNotification with the result of the operation. Home view controller which you have made listening to such custom notification, react to that and display error message or other staff.
It may sounds complicated, but KVO and notification are easy to implement, and there are plenty of docs and tutorial on the net.
If there is a clear 1:1 mapping of what those delegate protocols provide AND the delegate does not deal in UI stuff that nothing except the directly owning view controller should be concerned with, you could pass the delegate along to the end of the chain and set it directly as a delegate. This is what delegates are for - being able to allow an otherwise unconcerned object to communicate with another object.
That said, depending on how strict your layering policy is, you may prefer to encapsulate the information at every step by having different delegates.