I have submitted my app and been accepted couple of weeks ago. However, I have an issue that one of my critical functionality which is adding pin is not working but it works in building version. Adding pin requires URL connection. Could you please look at the answer that I get from apple developer and guide me please?
It shouldn't be causing your problem (at least not directly), but in general
having networking code like this live inside your UI code is very problematic
for a number of reasons:
-On general architecture grounds, it's a large violation of the MVC (model
view controller) architecture. The pin annotation is obviously heavily tied
to your UI (the view), while networking is clearly part of your backend (the
model).
-Because iOS REQUIRES the main thread to remain responsive, attaching code
that does IO or computation to the main thread is inherently risky. It can be
done and work well, but doing it incorrectly will inevitably lead to crashes.
NSURLConnection works in background and interactions with UI should go on main thread.
So, if you add your pin at the connectionDidFinish method for example, you should do like this:
[mapView performSelectorOnMainThread:#selector(addAnnotation:) withObject:yourAnnotation waitUntilDone:[[NSThread currentThread] isMainThread]];
Hope that helps you. I wrote from windows but the line should work.
I'm not exactly sure about what you are trying to achieve. Are App folks complaining because you are fetching URL directly in the Main thread ? Could you just send a code snippet showing how you fetch urls ?
One option would be to drop the pin (not tied to URL connection at all) and once the pin drops, load your data for the callout or annotation (which is what I'm assuming you are doing). You can always put a loading indicator or progressHUD up to tell the user they need to wait for something. It isn't exactly ideal, but in instances where you need to wait for data, what are you going to do?
Apple really doesn't care about blocking the UI so long as the user is aware you are blocking it. Like a progress bar or progress HUD. The user can see the app is still working and doing something. Not showing anything and having the UI just freeze while it waits for the main thread/main loop to return isn't going to work. It is just bad design and Apple will likely ding you for it.
Related
I have a project where I have 4 tab bars, and when i switched tabs, my api to get the API request to update my view is in the method viewDidAppear().
However this will make a very bad UX experience for user as whenever they switch tab, a loading icon and some times it load for 0.5 seconds but that instant appearance and disappearance of the loading icon is really bad for UX perspective. How can i implement a view where whenever the database value change, my view is automatically updating itself?
I have thought of using Timer() to implement where it calls the api every second but this is not practical as it will be constantly calling the API every second.
Does anyone have any suggestion? Thank you
There's a lot to unpack here, but I'll try to provide a generalized answer and point to several possible solutions to the UX problem.
There's several aspects that can play a role here:
Do you control the backend the data comes from, i.e. is it possible to follow paiv's comment and implement push notification? Or perhaps use some other means than pure HTTP(S) requests, like a websocket?
How frequently does data change? Is it realistic that it does so in between switching from one tab to another?
On that note, what do you do when data changes while your user is on one tab? Does that get refreshed in any way? Is it a problem if they don't get informed about this immediately? Is there a refresh button?
Do you persist the data beyond a view's on-screen time at all? Is it scrapped when a user re-visits a tab, i.e. does a tab always start "empty" until the API response is handled or do users see the values from the last time they visited that tab?
What best to do depends a lot on the exact answers to these questions. If you cannot enable the backend to do anything besides serving HTTP requests AND have to keep all data up-to-date constantly then obviously you have to do something like the polling you described. The update-interval depends on how important this is for your users. Even if it is 1 second, be aware that this means they could visit a tab before the next update has been fetched.
If you can adapt the backend and up-to-date data is a must, then push notifications or perhaps websockets are a way to go (keep in mind that websockets mean one open-connection per active user then!).
Push notifications would probably break down when the update interval is too high (i.e. if data gets changed in the backend very quickly and you keep spamming them), but would work nicely otherwise, plus they would update views that are already on screen if used correctly.
In 90 % of the cases I've seen, however, going with regular HTTP requests and a properly designed UI is the best choice. Properly decoupling the UI code from the data loading code and the code that displays the data is also very important. Here's how I would handle this:
Properly persist the data in some way in the app that does not require on views owning the data alone. If you want to persist beyond app runtime that means CoreData (or an equivalent), otherwise an in memory storage may be fine (that is kind of a global state then).
In your UI, show any data you currently have, even if it is "outdated" (in your case: "left over from the last time the user was on this tab"). Design the UI so that this is clearly apparent (grey it out, put tiny activity indicators next to the data while it is being loaded anew/updated, or something similar).
As said in 2. do show if the app is loading data. Initiating the loading when the users go to a tab is okay, but don't make a UI that is completely dominated by it (like one giant, empty view with a spinner on it).
Functionally, the loading should not directly update any views, instead it writes new data to your storage and just informs views and view controllers that they need to update themselves (if you're using CoreData and/or Combine you get a lot of this logic basically for free, especially for a SwiftUI app).
Such a design also lends itself to later being adapted for push notifications: While at first the re-loading is triggered by a tab switch (or a "refresh" button, for example), once (silent) PNs are available, they trigger the reloading. Even websockets can be integrated into this more or less, though there's probably a bit more to do that.
In any way, as said it all depends on your use-case and also available resources, but the gist of it is probably "If the current loading icon you have destroys the UX, redesign it properly". My gut feeling here would be that anything else might be over-engineering for your needs...
I am currently programming a Quiz-App for iphone using swift and xcode. Since a week I a stuck with a problem, i wasn't able so solve up to today.
Little Background Information:
All Questions are stored online on a server and are downloaded just in time. The Questions always contain images, so it takes some time to load them. This is why I am pre-loading the next question in background while the user is still thinking about the current question. Therefore I use the class QuestionLoader which extends Thread.
The Problem Situation:
When the user answers a question and presses the continue-button, the function goToNextQuestion() is triggered which shows the next question to the user. Either the pre-loading of the next question is finished and it can be shown instantly or the next question is still loading and we have to wait.
The Problem:
Now if we have to wait, I want to show a Loading-Label and -Indicator. Both are in the gui (placed with interface-builder) and hidden at the time. I do it like this:
func goToNextQuestion()
{
//check if next question is already ready
if (nextQuestionLoader.isFinished==false)
{
//not ready, so wait and show waiting indication
labelLoading.isHidden = false
loadingIndicator.startAnimating()
while(nextQuestionLoader.isFinished == false) {}
//question i now ready, hide waiting indication
labelLoading.isHidden = true
loadingIndicator.stopAnimating()
}
//so next question is ready and all good
}
What I want to happen is that when the user pressed continue and the next question is not ready yet, that the loadingIndicator and the labelLoading are shown until the question is ready.
Now what happens is that continue is pressed the there is nothing shown and the gui is blocked util the question is ready, then it shows the next question and the same thing happens with the following question.
Screenshots of the area of my gui that is concerned for better visualitzation
(Sorry for the different number of points gained in the screenshots, does not have to do with the problem, simply 2 different screenshots)
So the gui update does not happen before the question is loaded completely and does not happen immediately as it should!
Additional Information
I tried the showing of the loading indicators at other points of the code and they show correctly, so there is no problem with this.
When i put print-command before and after the while-loop and follow the output in the terminal, I see that the print-commands are correctly executed before and after the while-loop. But the gui-update does only happen afterwards
The function goToNextQuestion is completely executed in the main-thread!
So how can i achieve that the loading-indication is shown directly and after it is shown, the while-loop waits for the finish of the other thread?
I am really desperate because i have no idea at all how to solve this.
I would really really appreciate some help from you!
Thank you very much in advance for your time!!
There are several problems with your approach.
First, you should not use NSThread/Thread. The system class URLSession handles async downloading for you. (And creating threads is expensive and error-prone.) Use URLSession instead. You should be able to find lots of examples online, including here.
Second, you can't block the main thread like you're doing with your
while(nextQuestionLoader.isFinished == false) {}
Code. Since your download is running on a background thread it will keep running, but by blocking the main thread everything will freeze until the download completes. Your UI changes don't get a chance to be drawn on the screen until the download is complete.
What you should do is use URLSession using either a delegate method or a completion closure. It supports both approaches.
I have a project called Async_demo on Github that includes working code that shows how to use URLSession. It's a simplified example, but it should get you started.
I'm sorry if this post is in the wrong area, please advise me on where to move it if needs be.
I've just been passed a project from a previous developer and to be honest the repository is horrendous, the previous developer used awful naming conventions, the code is completely serial, no XCAssets, XIB's or Storyboards were used and I'm finding the whole project very hard to navigate - oh and no comments! What sort of developer leaves no comments...
So far it has taken me 4 hours to fix problems that would normally take a matter of minutes due to having to scour through 200 different source files.
I was wondering if there is a way to tell the debugger to stop each time a function is called on a click events - basically on the UI there is a button which is displaying the wrong dialog, due to awful naming conventions I am finding it near impossible to locate the place in the source for me to make changes.
Any advice would be appreciated (I have told the team I am planning to rebuild the whole app, but we are due to launch next week).
Welcome to the world of programming. You are able to add a custom forwarding delegate to the AppDelegate on the Main start up that will intercept events such as the ones you described above via
int retVal = UIApplicationMain(argc, argv, #"MyListenerHere", nil)
My iOS app relies heavy on server side data, and just for the launch of it, I need a little bit of information from Parse to get the job done on the app delegate... the issue is I'm making this query on the main thread because otherwise I would use a block or a queue, and immediately after the start of the app, the launch image shows up, then the query starts and the screen goes blank, then the query arrives and the app screen refreshes and is ready to go, but this looks very odd for the user experience and I don't want it to happen..
With the query on the main thread the launch image stays until the data arrives, and it looks much better and the loading time is about 2-3 seconds...
It feels like a bad practice, but...
Any advices?
Regards,
Miguel Rojas Cortés
Don't block the main thread when the app launches. If the network request isn't fast enough, the watchdog will terminate your app and your users will give you 1 star reviews.
Just display your UI with as much info as you have, and show some visual indication that more data is loading. Then update the views when the data arrives.
Also remember to handle the case that the user launches your app with no connectivity. The user should get an appropriate error and an option to retry.
You just need to do a bit more work here
Create a separate nib / view controller for the launch screen instead of the using the default iOS one
When the launch view controller loaded, starts the request, and don't do any transition just yet. Maybe show some kind of loading indicator there.
When all data has arrived, do a transition to the first screen (either fading smoothly or abruptly, IDK).
Doing query on main thread in this case may work 90% of the time, but the other 10%, eg when network is flaky, it's not a nice experience. The app will just hang there, and you got no chance to handle returned errors, since the main thread is blocked.
I have an iPhone app which pretty much is a mobile app for a website. Pretty much everything it does is call API methods from our server. The app retrieves the user's information, and keeps updating the server using the API.
My colleague and I had a discussion whether to introduce GCD to the downloading aspect on the app. My colleague argues that since the UI needs to wait for the download to complete before it can display the pictures, text or whatever, there is absolutely no need for GCD. My argument is that we should keep the main thread busy with UI rendering (even if there is no data), and introduce GCD to the app to create other threads for download.
Which argument is right here? In my case, if the UI renders with no data, will there be some sort of lag? Which will yield a cleaner, sleeker and faster app?
One argument would be : what will happen when the download fails and times out because there is a problem at the server end ?
Without GCD the app will remain blocked and will crash after a time
out since the UI can not be blocked for longer than 20 seconds.
With GCD the application remains functional but there will be no data
being downloaded and the application will not crash.
Other aspects to take into account are :
the thread safety of the objects that you are using
how you handle downloads that are no longer necessary because the user navigates away from the page
I don't think doing time consuming operations in the main thread is a good idea.
Even if user have to wait for the data te be downloaded before he can do anything meaningful, still he will not hope UI is blocked.
Let's assume you have a navigator view, and after user tap some button, you push a new view to it and start download something. If user suddenly decides he don't want to wait anymore, he tap the "back" button. If your downloading operation blocks UI, user will have to wait it to end, it's really bad.
A more appropriate question would perhaps be if you should download asynchronously or on the main thread for your app, since there are several different methods to download asynchronously on iOS (e.g. using NSThread, NSOperation or indeed GCD). An easy approach to achieve your goals could be to use the AFNetworking library. It makes multithreaded networking / internet code very easy to implement and understand.
Personally I'm very fond of GCD and recommend you learn it someday soon, though in itself it is not as suitable for asynchronous downloading compared to a library like AFNetworking (that uses GCD under the hood, I believe).
Here is a good read on using NSOperationQueues (that uses GCD behind the scenes) to download images. There is also some Github code you can check out. They have an elegant solution to pause downloads and enqueue new downloads when the user moves to different parts of your app.
http://eng.alphonsolabs.com/concurrent-downloads-using-nsoperationqueues/?utm_medium=referral&utm_source=pulsenews
Use GCD / NSOperationQueues as opposed to using NSThreads. You will have a good learning on core fundamentals and at the same time create a well architectured app. :)