Asynchronous download of multiple images - ios

I am new to ios app development.could you please tell me how to download two or more images asynchronously using NSURLconnection. I can do it for one one image but seeks some help to do it for two or more images.

To do this I usually have a helper class which downloads an image given a URL that calls me back when it's done. I've actually released that code as part of my MJGFoundation project. Take a look at the class called MJGImageLoader.
Essentially it's very straight forward and what I do is wrap all the NSURLConnection handling into a single class which you then start by passing a block which will be run on completion. I suggest you either take my code there and use it or look at how I've done it and replicate it yourself according to your needs.

create a separate class lets say - ConnectionManager having NSURLconnection complete implementation. create a protocol in the same class.
in the controller, make object of ConnectionManager pass delegate as self and start downloading one file. on completion, ConnectionManager can trigger your delegate and can deliver you the downloaded data.
to download multiple files simultaneously, you need to make multiple objects of ConnectionManager and start your requests. With that you will also require to handle the delegate as to recognize, which request is triggering your delegate.
hope it'll help somewhat.

Related

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.

Send Progress from NSURLSession to ViewController [swift - iOS]

I'm developing an app which handles some downloads in the background. I move the file to the documents directory and save it under the original name (using downloadTask.originalRequest?.URL!.pathExtension). So far so good.
Using
URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
I can print out the progress in the console while downloading. But this doesn't help me if I'm using the App on the phone. What I'm expecting to do is to call a function to update a progress bar from here within my View that was initialising the download. But I have no idea how to call it.
Does anybody know how I can find out from which UIViewController (actually most of the time it will be a TableCellViewController) I started the download and how to send the progress to a function of that ViewController?
I 'd like to tell you a good tutorial about this question. NSURLSession Tutorial
Delegates are often used in the following situations
When a class needs to communicate some information to another class
When a class wants to allow another class to customize it
The classes don't need to know anything about each other beforehand except that the delegate class conforms to the required protocol.
I the following article you can see how to create a delegate in objective-C and Swift 2.0.
How do I create delegates in Objective-C?

Best way to handle a JSON call and use it's information in all views

I have a API serving a JSON with all the information needed to put into my app, and it will serve with texts and all the news needed for all the views in the app.
This information and JSON will never be big enough that i need to make multiple calls or even paginate, but the information might be changed in the future since it's an app for an event, and some minimum stuff will be added to the information like, sponsors logo, and some stuff like that.
I was wondering, what is the best way to handle this? Make an API call on the appdelegate or something, save it local and make one API call per day to "update" this local file?
Also, technically speaking, how do i handle the information for all the views?
Keep reading this local file everytime a view is loaded?
implement a singleton class that holds your data inside proper data structure(s). you need to download the JSON in the root view controller and display an activity indicator to the user. in every view controller you can access the singleton class object asking for the data relative to you controller.
I wouldn't put it in the app delegate -- the app delegate should remain small and only deal with APP specific issues and to do
generally you want to keep the Separation of Concerns (dijkstra ;))
I would have a singleton MyDataManager that exposes dataWithCompletion: and in that method, decide if you return the cached data (saved locally) or make an HTTP Request to update it)
For starters you can make a shared instance (singleton) to handle your API calls with AFNetworking or what ever you prefer.
Doing this only once a day is not a bright idea if the response is very small, eg. 5000 characters is only 5kb. You can download and save the images once. So I would prefer calling your API every time the app launches in didFinishLaunchingWithOptions.
To handle the views from your response, you need to make sure your JSON response structure (keys) are always the same.
Example: self.eventTitle = [response objectForKey:#"event-title"]; where "event-title" should never change, your app will crash or give a (null) value.
You can create a singleton object with the json serialize method. The you can instantiate your method to serialialize a JSON that is inside your singleton object wherever you want.
You can follow this guide: http://www.galloway.me.uk/tutorials/singleton-classes/

Where is the correct place to make HTTP requests in iOS?

I have an app that fetches data from a server using NSURLSessionDataTask. As of right now I am starting my HTTP GET Request in the init method of the UIViewController that displays the data. Is this the best/smartest place to kick off an HTTP request? If not, where should I do it?
I'm asking this question because when I exit my app and it goes into the background (and is not killed) and then re-open my application, the HTTP request is not fired off (because it is in the UIViewControllers init method) and the data being displayed is not up to date with what's on the server. I've tried putting it in viewDidLoad but this method is not called upon entering the foreground, neither is viewWillAppear nor viewDidAppear.
Should I be doing all of my HTTP requests in one of the UIApplications life cycle methods in my appDelegate?
In short, where is the best place to make HTTP requests in iOS?
Thank you, I can post code or explain more if needed.
first off, this is a huge question and probably impossible to answer fully here, but hopefully I can point you in the right direction so you can learn how to fish. :)
To stick to the Model-View-Controller paradigm, you will want to create a separate object for making your HTTP requests. An HTTP request would be considered part of your model. The benefit of this is being able to use your model in other iOS apps you create, for example.
As for where to put all of this stuff and what's the best design.... One thing that strikes me in your question is you want the data being displayed in your app to be up to date with what is on the server. On a high level, a really good way to do this is to use iOS's ability to multitask and perform functions for you when you app is in the background. You would need to register with the OS as an app that performs fetches to a server in the background.
According to Apple's documentation, "In Xcode 5 and later, you declare the background modes your app supports from the Capabilities tab of your project settings. Enabling the Background Modes option adds the UIBackgroundModes key to your app’s Info.plist file." From there you would need to research the UIApplicationDelegate's protocol methods – application:performFetchWithCompletionHandler: and -application:handleEventsForBackgroundURLSession:completionHandler:.
Also, you will need to look into NSURLSession a little more. If you want to use background fetching, NSURLSessionDataTask is not supported. You will need to use NSURLSessionDownloadTask, save the response to a file and process it however you need to. Also, as the app delegate method name above implies, you will need to read the NSURLSessionConfiguration Class Reference, specifically about backgroundSessionConfiguration.
The really cool thing about all of this is, after you have implemented it, your app UI will be up to date for the user – even if your app was killed by the user or by the OS. Pretty nifty.
Good luck and hope this all helps. I hope I didn't miss any other big pieces here.
I think it a personal preference. So I personally do it on the model objects. Lets say I have a Car object and a ShowroomViewController. I always declare a class method to Car object to call service to get all the cars.
#interaface Car
+(void)fetchCarsWithCompletionHandler:(void (^)(NSArray* cars, NSError *error ))handler;
-(void)getDetailsWithCompletionHandler:(void (^)(Car* car, NSError *error ))handler;
#end
Then call the class method on viewWillAppear(If I need to update the cars very often) or viewDidload(If I need to call the service once).
The other trick I mostly do is define a flag in the view controller like
#interface ShowroomViewController
#property(assign)BOOL needsModelUpdate;
#end
and I update the modal conditionally.
#implementation ShowroomViewController
-(void)viewWillAppear:(BOOL)animated{
if(self.needsModelUpdate){
[self fetchModel]
}
}
-(void)fetchModel{
__block __weak ShowroomViewController *weakRef=self;
[Car fetchCarsWithCompletionHandler:^(NSArray *cars, NSError *error) {
[weakRef setCars:cars];
[weakRef.tableView reloadData];
}];
}
#end
The reason I define this flag is I can change it somewhere else lets say applicationDidEnterBackground: method the change the flag. Or you can use KVO but I always find it overkill .
Do it however you want.
Personally, I create a class specifically for all communication with the server. Actually, my App has around 20 classes for different parts of the communication process. But yours is probably less complicated.
Add an instance of the class as an object in an xib file or else create an instance of it inside the app delegate's init method.
Use didEnterForeground to tell the other class that it needs to do it's stuff, but still use the init method to create an instance of the class.

Registering setup code in objective-c

Is there a standard mechanism with Objective C and the iOS runtime to register setup code?
Why?
The advantage of this is that you can decouple your code nicely.
If a subsystem needs particular setup, the set up stays in that subsystem.
If a group of files need to register that they should all be offered as a particular service, that can be encapsulated in individual files that offer the service and there is no need for a separate configuration file to be kept up to date.
Getting the code to run isn't especially important – I can do that myself with various entry points. What I need is for the compiler or linker or run time or magic to be able to collect up anything that I've registered in different parts of a program, and let me have it when I need it.
How to in c++
With c++, I've typically arranged for this with static instances that are constructed before main() is called. I could use objective-c++, but I'd much prefer to use a standard mechanism.
Thanks.
I would look up:
+ (void)initialize
This method is called whenever a message is first sent to the class meta-object, such as, for example, when alloc-ing an object of that class.
Registering Code
Use the class method +(void) load for components that need to self register themselves.
Note that the load method is run on all subclasses and all categories. This is nothing like the the normal method calling behaviour.
Creating a Registry
If components need to register themselves in some kind of container, use the class method +(void) initialise to create a container to hold the components that are going to register themselves. It seems from my limited testing that initialize can be called before load when a load method uses a class with an initialize method, which is pretty cool if reliable.
Running Registered Code
If the components should do something at some specific entry point of your App, then at that entry point, grab the registered components from the registry and do that thing. Eg, you might extend you UIApplicationDelegate's -(BOOL) application:didFinishLaunchingWithOptions: to actually perform the setup stages the components registered.
In my case, I actually want the registered code to get run every time a specific kind of object is constructed, so I call the registered methods there and let them have the object being constructed.
More references on load and initialise
Thank you for the answers and comments that let me put this answer together.
Quite a lot of detail from Mike Ash, although I was initially put off by his statements about load being "tricky because it runs so early".
A very helpful S.O. question on load and initialize.

Resources