I would like to perform transactions only when webView is completely loaded.
I'm writing down the following code.
However, NSLog(#"completely loaded") is called several times.
- (void)webViewDidStartLoad:(UIWebView*)webView
{
self.webViewLoadingCount++;
}
- (void)webViewDidFinishLoad:(UIWebView*)webView
{
self.webViewLoadingCount--;
if (self.webViewLoadingCount > 0) {
// loading
return;
}
// completely loaded
NSLog(#"completely loaded");
}
Could you tell me how to solve this problem?
Thank you.
Related
My users experienced crashes when I sent them an update on TestFlight. After examining the eight crash reports they submitted, I've noticed a commonality - there are two of the same closures sitting on top of thread 0. Could this have caused the crash? What do you think is the cause, if not?
Please see image for crash report of thread 0. All other threads generally look the same in the report.
Note - when the users opened their app subsequent times after the initial opening, they did not experience further crashes.
Thank you in advance for your thoughts.
Update from comments, 9/29/22 -
Here's the closure code as requested by Damien and Tadreik:
When the app is opened, this line runs during initialization, which sets up the variables the connection view controller needs. Thus the empty closure.
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { _ in }
}
And the code below is invoked when the connection view is tapped on from the menu tab the second time:
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { result in
guard let success = result else { return }
if success {
self.handleSuccessOfGettingConnectionCards()
}
else {
self.handleFailureOfGettingConnectionCards()
}
}
}
Here is the code for handleSuccessOfGettingConnectionCards -
refreshControl.endRefreshing()
hideNoConnectionsLabel()
createChatViewControllersForChannels()
connectionsTableView.alpha = 1
errorConnectingLabel.alpha = 0
connectionsTableView.reloadData()
showActivitySpinner(false)
navigationController?.navigationBar.isHidden = true
Here is the code for handleFailureOfGettingConnectionCards -
showErrorConnectingMessage()
refreshControl.endRefreshing()
connectionsTableView.alpha = 0
hideNoConnectionsLabel()
showActivitySpinner(false)
Thanks in advance again for any intuition or experience you may share.
The crash log for thread 0
The code inside the closure ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { result in captures a reference to self, your problem might be that at the time of execution, self is pointing to a null reference (has been deallocated), which can cause a crash. Try to set a weak reference to self like that and see if the crash still occurs :
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { [weak self] result in
guard let success = result else { return }
if success {
self?.handleSuccessOfGettingConnectionCards()
}
else {
self?.handleFailureOfGettingConnectionCards()
}
}
}
If you want you could also handle the nil case of the weak self as any other optional.
I'm having an issue with the setContainerView, the progress is not showing at all when I assign it to a custom view. I've tried everything with my UIView which is perfectly fine, but the progress remains invisible.
I've used it for a while now and had no problems without custom container views.
Any ideas ?
EDIT
For exemple, this is working (progress showing):
func download() {
SVProgressHUD.show()
User.downloadAllUsers { (user) in
//Doing things
DispatchQueue.main.async {
//Updating UI
SVProgressHUD.dismiss()
}
}
}
But this is not working (progress is not showing)
func download() {
SVProgressHUD.setContainerView(loadingContainerView)
SVProgressHUD.show()
User.downloadAllUsers { (user) in
//Doing things
DispatchQueue.main.async {
//Updating UI
SVProgressHUD.dismiss()
}
}
}
Purely FWIW, in your sample code, I think you're dismissing at the end of each operation rather than at the end of all operations ... I believe.
You'd probably use there a DispatchGroup as the solution.
A quick tutorial on using DispatchGroup would be beyond the scope of this QA.
I'm trying to write a series of functions that will validate the user's information before asking them to confirm something. (Imagine a shopping app).
I first have to check that the user has added a card.
Then I have to check that they have sufficient balance.
Then I can ask them to confirm the payment.
I can write the async method to check the card something like ...
func checkHasCard(completion: (Bool) -> ()) {
// go to the inter webs
// get the card
// process data
let hasCard: Bool = // the user has a card or not.
completion(hasCard)
}
This can be run like this...
checkHasCard {
hasCard in
if hasCard {
print("YAY!")
} else {
print("BOO!")
}
}
But... now, based off that I have to do various things. If the user does have a card I then need to continue onwards and check there is sufficient balance (in much the same way). If the user does not have a card I present a screen for them to add their card.
But it gets messy...
checkHasCard {
hasCard in
if hasCard {
// check balance
print("YAY!")
checkBalance {
hasBalance in
if hasBalance {
// WHAT IS GOING ON?!
print("")
} else {
// ask to top up the account
print("BOO!")
}
}
} else {
// ask for card details
print("BOO!")
}
}
What I'd like instead is something along the lines of this...
checkHasCard() // if no card then show card details screen
.checkBalance() // only run if there is a card ... if no balance ask for top up
.confirmPayment()
This looks much more "swifty" but I'm not sure how to get closer to something like this.
Is there a way?
Asynchronous operations, ordered and with dependencies? You're describing NSOperation.
Certainly you can chain tasks using GCD:
DispatchQueue.main.async {
// do something
// check something...
// and then:
DispatchQueue.main.async {
// receive info from higher closure
// and so on
}
}
But if your operations are complex, e.g. they have delegates, that architecture completely breaks down. NSOperation allows complex operations to be encapsulated in just the way you're after.
So I'm looking at the Photos API and Apple's sample code here
https://developer.apple.com/library/ios/samplecode/UsingPhotosFramework/Introduction/Intro.html
and its conversion to swift here
https://github.com/ooper-shlab/SamplePhotosApp-Swift
I have integrated the code into my project so that a collectionView is successfully updating itself form the library as I take photos. There is one quirk: Sometimes cells are blank, and it seems to be connected to stopCachingImagesForAllAssets which Apple calls each time the library is updated at the end of photoLibraryDidChange delegate method.
I can remove the line and it fixes the problem, but surely there is a reason Apple put it there in the first place? I am concerned with memory usage.
// MARK: - PHPhotoLibraryChangeObserver
func photoLibraryDidChange(changeInstance: PHChange) {
// Check if there are changes to the assets we are showing.
guard let
assetsFetchResults = self.assetsFetchResults,
collectionChanges = changeInstance.changeDetailsForFetchResult(assetsFetchResults)
else {return}
/*
Change notifications may be made on a background queue. Re-dispatch to the
main queue before acting on the change as we'll be updating the UI.
*/
dispatch_async(dispatch_get_main_queue()) {
// Get the new fetch result.
self.assetsFetchResults = collectionChanges.fetchResultAfterChanges
let collectionView = self.pictureCollectionView!
if !collectionChanges.hasIncrementalChanges || collectionChanges.hasMoves {
// Reload the collection view if the incremental diffs are not available
collectionView.reloadData()
} else {
/*
Tell the collection view to animate insertions and deletions if we
have incremental diffs.
*/
collectionView.performBatchUpdates({
if let removedIndexes = collectionChanges.removedIndexes
where removedIndexes.count > 0 {
collectionView.deleteItemsAtIndexPaths(removedIndexes.aapl_indexPathsFromIndexesWithSection(0))
}
if let insertedIndexes = collectionChanges.insertedIndexes
where insertedIndexes.count > 0 {
collectionView.insertItemsAtIndexPaths(insertedIndexes.aapl_indexPathsFromIndexesWithSection(0))
}
if let changedIndexes = collectionChanges.changedIndexes
where changedIndexes.count > 0 {
collectionView.reloadItemsAtIndexPaths(changedIndexes.aapl_indexPathsFromIndexesWithSection(0))
}
}, completion: nil)
}
self.resetCachedAssets() //perhaps prevents memory warning but causes the empty cells
}
}
//MARK: - Asset Caching
private func resetCachedAssets() {
self.imageManager?.stopCachingImagesForAllAssets()
self.previousPreheatRect = CGRectZero
}
I was having the same result.
Here's what fixed the issue for me:
Since performBatchUpdates is asynchronous, the resetCachedAssets gets executed possibly while the delete/insert/reload is happening, or even between those.
That didn't sound nice to me. So I moved the line:
self.resetCachedAssets()
to the first line of the dispatch_async block.
I hope this helps you too.
I am working on vungle video integration in cocos 2d and and running the ad successfully.
In app i want to provide some points for watching video.. which i can set up.. but the problem is how to check whether the user has watched the video or close it ..
https://v.vungle.com/dev/ios
I am using the above help.. but not able to make it work..Please help me in checking whether app is ads are close or watched completely
- (void)vungleSDKwillCloseAdWithViewInfo:(NSDictionary *)viewInfo willPresentProductSheet:(BOOL)willPresentProductSheet {
//Verify that the view was completed before rewarding the user
BOOL completedView = [[viewInfo valueForKey:#"completedView"] boolValue];
if (completedView) {
//View was successfully completed, do your stuff...
}
}
Few months back I did use vungle. Check below code, that worked for me
-(void)vungleMoviePlayed:(VGPlayData*)playData
{
if(playData.movieTotal == playData.movieViewed)
{
printf("Video fully watched\n");
mSeenVungleVideo = true;
}
else
{
printf("Player aborted video at middle\n");
}
}
This seems to have changed since NatureFriend's answer. It's now:
- (void)vungleSDKwillCloseAdWithViewInfo:(NSDictionary*)viewInfo willPresentProductSheet:(BOOL)willPresentProductSheet;
{
// video done here.
}
It's possible at this point it will show a "Product Sheet". If you want to know when that closes, you can implement this delegate:
- (void)vungleSDKwillCloseProductSheet:(id)productSheet
{
// product sheet closed here
}