Store User Token, If Invalid token - present Login page, - ios

Learning iOS Swift programming and like to know how to implement user login process ?
The backend-iOS mechanism is this :
User login with email and Password,
The Server returns user token and user id
In subsequent requests, user token and user id is sent to fetch data/work with the App.
I have doubt in iOS implementation.
Will be storing User token and User id in Core Data. Will there be
any slowness if I get the user token on every screen from Core
Data?
If the login token expires or is invalid on any screen, how to fall back to login page? Should I check the JSON output and have code "present login VC" on every screen? Any streamlined way to have abstract the code to a swift or cocoa touch file?

Actually, there are many approaches. It's all depends on you, how you will manage it. I can point you two examples, how I manage it by myself.
Using NSOperation.
There was an awesome session on WWDC 2015, about advanced NSOperations. Here it is.
Basically, you create a subclass of NSOperaton and make other operations depend on it. In your case, you will have operation of user login, and all other operations will depend on user login (of course only ones, who needs it). If it succeed, then operation will execute. In user login operation, you will check, if user already logged in, and you have a token, if not, present logging screen.
There is also awesome library called Operations, based on that WWDC talk. Here it is.
Using PromiseKit.
Here is another one, using PromiseKit. It is not really much difference from operations, but, in my opinion, a little simpler. You create Promise that ensures that you did login. It is very simple to make a chain of promises, so you promise a user login and chain anything else from it. If login succeed, chain of promises continues executing.
It's all based on another awesome library, PromiseKit. Here it is
It is very powerful and very simple to use, once you understand the thing. It is very well documented, and has a bunch of tutorials here.
There are many other approaches, so you can choose any of it, and combine one with other however you like.
Asking on your first question, you make it asynchronous, so it is not so much matter about slowness of CoreData as you make a web request.

1 - well, yes, a bit. But I doubt you will notice any lag or anything: CoreData is really fast and it won't take any significant amount of time to fetch just one object. Alternatively, your CoreData object that holds this data (let's call it User) can be a property of your subclass of UIApplicationDelegate (let's call it MyAppDelegate). You will fetch this User in
- application:didFinishLaunchingWithOptions:, and update it on login/logout/expire/etc. In such way you can access it from everywhere, and you don't need to fetch it from CoreData anytime you need it.
Also, instead of CoreData you can use iOS Keychain. (Here is tutorial that might help with this).
2 - again, you can use MyAppDelegate. For example you can add method to it, that will save current navigation state, and change root controller of UIWindow to your LoginViewController. Or it will present LoginViewController on top of current controller. After successful login you will return navigation stack to previous state.
Also you can move this code to some kind of NavigationController - class that will handle this situation. It will have something like
func checkToken(Dictionary response, UIViewController currentController) -> Bool. If token is valid, it just returns true, and if not, it returns false and handles navigation to LoginViewController, and after login - back to currentController. This NavigationController can be property of MyAppDelegate, it can be singleton, created every time you need it, or you can pass it along to every UIViewController that you show.

Related

VIPER architecture and proper place to save access token

So, lets say that this is a general definition in VIPER architecture:
The main parts of VIPER are:
View: displays what it is told to by the Presenter and relays user
input back to the Presenter.
Interactor: contains the business logic as specified by a use case.
Presenter: contains view logic for preparing content for display (as
received from the Interactor) and for reacting to user inputs (by
requesting new data from the Interactor).
Entity: contains basic model objects used by the Interactor.
Routing: contains navigation logic for describing which screens are
shown in which order.
Now in my interactor, I do a login network request, and I get the response that has access token (that I later save in keychain using my KeyChainManager).
Now, were would be a good and a proper place to save the token? Immediately in the interactor, or to pass it somewhere further maybe, eg. presenter... ?
This is a service so that's what the interactor is for. You should have a service for doing the request and a service for storing or retrieving the token, and only an interactor should be allowed to speak to them or even know of them.
That is, in fact, the whole point of the interactor — it's so that the presenter does not know anything about this. No one else should know anything about what's going on. A presenter should know nothing about the token — that it exists or where it is stored. That's all just behind-the-scenes implementation.

Keeping a WKWebView and it's UIViewController in the background running and accessible from multiple ViewControllers

Background: In order to make web requests to an API endpoint, I need to scrape a website and retrieve a token every 25-30 seconds. I'm doing this with a WKWebView and injecting some custom JavaScript using WKUserScript to retrieve AJAX response headers containing the token. Please focus on the question specifically and not on this background information - I'm attempting this entirely for my own educational purposes.
Goal
I will have different 'model' classes, or even just other UIViewControllers, that may need to call the shared UIViewController to retrieve this token to make an authenticated request.
Maybe I might abstract this into one "Sdk" class. Regardless, this 'model' SDK class could be instantiated and used by any other ViewController.
More info
I would like to be able to call the UIViewController of the WKWebView and retrieve some data. Unless I re-create it every 25 seconds, I need to run it in the background or share it. I would like to be able to run a UIViewController 'in the background' and receive some information from it once WKWebView has done it's thing.
I know there are multiple ways of communicating with another ViewController including delegation and segueing. However, I'm not sure that these help me keep the view containing the WKWebView existing in the background so I can call it's ViewController and have it re-perform the scrape. Delegation may work for normal code, but what about one that must have the view existing? Would I have to re-create this WKWebView dynamically each time a different model, or view controller, were to try and get this token?
One post suggests utilising ContainerViewControllers. From this, I gather that in the 'master' ViewController (the one containing the other ones), I could place the hidden WKWebView to do it's thing and communicate to the child view controllers that way via delegation.
Another post suggests using AppDelegate and making it a shared service. I'm completely against using a Singleton as it is widely considered an anti-pattern. There must be another way, even if a little more complex, that helps me do what I want without resorting to this 'cheat'.
This post talks about communicating between multiple ViewControllers, but I can't figure out how this would be useful when something needs to stay running and executing things.
How about any other ways to do this? Run something in a background thread with a strong pointer so it doesn't get discarded? I'm using Xcode 9.2, Swift 4, and iOS 11. As I'm very new to iOS programming, any small code examples on this would be appreciated.
Unfortunately, WKWebView must be in the view hierarchy to use it. You must have added it as a sub view of an on-screen view controller.
This was fine for me. I added this off-screen so it was not visible. Hidden attribute might have worked as well. Either way you must call addSubview with it to make it work.
There are some other questions and answers here which verify this.
Here is a way if you don't wish to use a singleton.
1- In the DidFinishlaunchingWithOptions, Make a timer that runs in the background and call a method inside the app delegate Called FetchNewToken.
2- In FetchNewToken, make the call needed and retrieve the new token (you can use alamofire or any 3rd library to make the call easier for you).
Up on successfully retrieving the token, save it in NSUserDefaults under the name upToDateToken
You can access this token anywhere from the application using NSUserDefaults and it will always be up to date.

Detect deletion of Core Data

Extra info:
I have a messaging view in which I have a UITextView of which I save the text in the conversation's variable draft in the viewWillDisappear.
When the app tries to refresh the user's access code, they might get a "could not refresh" response, and the app logs the user out (only one device may be logged in at one time in this app).
In the logout method, I remove all app settings and empty out Core Data, then I set a new rootViewController and perform makeKeyAndVisible.
Question:
Now that you know all this, setting the rootViewController calls viewWillDisappear, which in turn tries to set the draft variable on a conversation that no longer exists in Core Data...
What can I do to solve this?
The simplest and fastest fix would be, when setting the draft:
if let context = conversation.managedObjectContext {
// you have a valid conversation, you can assign the draft
}
If the managedObjectContext is nil, this means the object has been deleted from Core Data.
EDIT
This answer provides a better way to detect if a managed object has been deleted from Core Data.
I would advise you to rethink the whole logout (clearing of resources) approach since yours is not going to scale in the future.

Having to agree to T&Cs on first run in Swift

I was wondering if it would be possible to programmatically make users agree to the Terms of Service I have for my application, but for them only to have to agree to it the first time they run the app straight after downloading it, as Apple requires Terms and Conditions for a social networking type application. Any help would be much appreciated as it will be one of the deciding factors on whether my app makes it to the app store or not.
Here a simple solution:
Put your to-be-called method in your delegate's application:didFinishLaunchingWithOptions, which means the T&C are shown to the user as soon as he starts the application. Then for the "once-in-a-lifetime" you could simple store a value in NSUserDefault(e.g. a string "alreadySent") at the end your custom method, which will persist over time as soon as the user doesn't delete application & data.
So the flow of event could be
App launch
Check: is the NSUserDefault value "alreadySent"?
No: Ok call method and show T&C
Yes: skip
This is a simple method which gives you a one-shot way to show the user what you want.
(Just google if you don't know how the NSUserDefault works, it's straightforward)

ios App improve network performance by requesting data up front

I have an application that I'm writing that pulls data from a few network sources:
1) list of blog posts (UITableViewController)
2) list of videos (UIViewController with an embedded UIScrollView)
3) list of images (UIViewController with an embedded UIScrollView)
Right now, there is a home screen with a menu and when you push one of the buttons, a destinationViewController (described above) is what loads the data on demand. I've noticed this is quite slow, especially when on a cellular data connection as opposed to wifi.
I was thinking about creating a class that requests all the data up front and kick it off every time the app is reentered. Does anyone have suggestions that could help me answer the following?
1) are there any classes, frameworks, or existing i code i can use to kick off these requests in a single place?
2) how do my destination view controllers (mentioned above) get the data?
3) how do my destination view controllers get informed that the data is ready if they happen to be invoked before the data is available?
4) is there a better strategy i should employ?
I appreciate the help.
Thanks,
jas
Off the top of my head, I would make the request class you mentioned and start all the request methods in applicationDidFinishLaunching in AppDelegate. I would also probably make custom NSObjects for each type of object you would be fetching and in each of your request class methods, convert the fetched data into said object, then cache each object to disk as they are downloaded. Then in your viewcontrollers, fetch the cached objects as needed.
When you are cacheing, make sure you cache each object with a key that will 100% be unique, because you are going to want to run a check on the current local cache before you start a new download. I would probably string together the file type and file name, and set that string as the key for the cached object.
To run the check on current cache in your request class methods, something that says "if current cache contains object for key:
uniqueKey... do nothing. If else, start the download and cache the object when finished."
Also run a check in your view controllers, because you're also going to want to handle the case where your view controller is requesting a cached object, but it hasn't downloaded yet. So something along the lines of "if current cache has object for key:key, great! use it. If else, start the download... cache it... then give me a call back here so i can use it while I display a loading message to the end user."
Im sure there are other scenarios that you are going to have to deal with, but that's the theoretical direction I would head in.
EDIT:
check out this, it will probably help you a lot. I think it also uses Ego Cache (if you dont want to write your own cache methods): https://github.com/Infusion-apps/Download-Manager
EDIT 2:
I also agree with #RyanDignards point #4. If possible, avoid fetching data you don't need. However, only you really know your UI/UX and app functionality, and my suggestion is assuming there is a good chance your end user is going to be using your app for the sole purpose of consuming the content you provide. So they are most likely going to be wanting to read the blog posts, watch the videos etc... The call is up to you, if you think there is more of a chance that the user will be viewing all the content than not, I would go the preload route, because nothing pisses off a user like having to wait.
1) RestKit is generally regarded as one of the the standard web interaction frameworks http://restkit.org
2) RestKit provides methods such as -[getObjectsAtPath: parameters: success: failure:] or post, which will provide the response in success. Additionally, it can convert the response directly into the respective objects for you with mapping.
3) Generally I would post a notification which is unique to that request, and any controller interested would get notified, with the response located on notification.object within the listener. Additionally most network requests provide for a callback handler where you could update the UI directly.
4) I would advise against preemptive loading since you're using resources for something that you may not actually use. My advice would be to breakdown your calls to the smallest possible level, then as the notifications are posted, that data would be inserted into the UI.

Resources