I have a simple app which 2 tabs, I am trying to you the bloc pattern to move data around.
Flow 1:
1. App starts bloc is created with a seeded value
2. Build method is run for the page and data shows
3. User switches to another tab
4. User switches back, in this case no data is shown as the steam has already be been read once.
One option would be to resubscribe to send the seed value again.
Flow 2:
1. App starts bloc is created with a seeded value
2. Build method is run for the page and data shows
3. User changes the filters - which fires of the stream data is loaded
3. User switches to another tab
4. User switches back, in this case no data is shown as the steam has already be been read once.
In this case, I want the filter to be kept in place and don't want to use the seed value
Bloc
class DemoBloc {
final DemoApi demoApi;
Stream<String> _results = Stream.empty();
Stream<String> get results => _results;
BehaviorSubject<String> _tabName =
BehaviorSubject<String>.seeded('abc');
// BehaviorSubject<String> _tabName =
// BehaviorSubject<String>();
Sink<String> get tabName => _tabName;
DemoBloc(this.demoApi) {
print('initialized');
_results = _tabName
.asyncMap((tab) => tab)
.asBroadcastStream();
}
void dispose() {
_tabName.close();
}
}
As I was using a behaviour subject this shouldn't have been an issue, upon investigation I found that the bloc was being created again in some cases, which resulted in the issue.
How I found
Added a log to the bloc constructor
Added a log to bloc provider - logged the caller as well
Result
When I dismissed showSearch - it trigger the build method of the parent which recreated the bloc.
Solution
Moved provider to one level above, so build method even if called multiple times won't cause the issue
This still didn't resolve it, because I was using a pushReplacement, pushReplacement caused the context to create a new bloc.
Finally moved the Provider to the top, so my material app was a child of the provider. As navigator is part of material this resolved the issue even when using pushReplacement.
Related
My app has a widget that allows for customization. When the user chooses to edit the widget, it flips to reveal a customization pane. For example, let's say I have a weather widget and the only customization option is the city for which to show the current weather condition.
When the user taps the current city to choose a different one, I need to perform a network request to fetch the available cities that might take some time under difficult network conditions. In that scenario, my widget configuration pane appears to be broken because nothing happens when I tap on the current city. Only after several seconds (when the data has been fetched), the modal screen appears with the list of available cities.
But I want to give the user some visual feedback that something is happening while the cities are loading. How can I achieve that?
Note
I found this other question on Stackoverflow with a GIF that shows a loading spinner (activity indicator / ProgressView) inside the widget configuration pane. How do I make this thing appear?
Sample Implementation
This is my sample implementation in my IntentHandler to simulate a network request before returning. (Tried the same thing using a completion handler instead of the async function.) It doesn't show a loading spinner.
func provideCategoryOptionsCollection(for intent: CityIntent) async throws -> INObjectCollection<Category> {
// Simulate async network request
try await Task.sleep(nanoseconds: 5 * Double(NSEC_PER_SEC))
let citiesSection: INObjectSection<City> = INObjectSection(
title: "Cities",
items: [.singapore, .sydney, .honolulu]
)
return INObjectCollection(sections: [citiesSection])
}
So I created an app using Ionic and Firebase as my back-end. When the app is run in a web browser or on an iOS emulator, the response is very fast and the app works really well. On iOS however, uploading anything to Firebase takes forever. Note that downloading information from Firebase is fairly fast and simple. Uploading however poses an issue. The wifi I am testing this on is very fast. Does anyone know why this is happening?
The app was released recently and this has been an issue for a lot of my users and myself included!
UPDATE: So after more testing it appears that the issue is specifically with certain functions. These methods are .update() and .add()
Anytime I try to update a field in Firebase it takes forever. Anytime I try to add a document to a collection it also takes forever. Why is this occuring? Here's some code that takes forever to achieve:
async createDMChat(otherUID: string, otherName: string) {
let newDoc: DocumentReference = this.db.firestore.collection('dmchats').doc();
let docId: string = newDoc.id;
let chatsArray = this.dmChats.value;
let timestamp = Date.now();
chatsArray.push(docId);
//Adds to your dm chat
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(this.dataService.uid.value).set({
chatsArray: chatsArray
});
//Adds to other person DM chat
//-------------------THIS IS THE PART THAT TAKES FOREVER-----------------------
//The .update() method is the problem as well as .add() to a collection
await this.db.collection('users').doc('dmchatinfo').collection('dmchatinfo').doc(otherUID).update({
chatsArray: firebase.firestore.FieldValue.arrayUnion(docId)
});
//Pull info on person's UID
let otherUserInfo = await this.db.firestore.collection('users').doc('user').collection('user').doc(otherUID).get();
let otherAvatar = otherUserInfo.data().avatar;
//Sets message in database
await newDoc.set({
chatName: otherName + " & " + this.dataService.user.value.name,
users: [otherUID, this.dataService.uid.value],
lastPosted: timestamp,
avatar1: this.dataService.avatarUrl.value,
avatar2: otherAvatar,
person1: otherName,
person2: this.dataService.user.value.name
});
await newDoc.collection('messages').doc('init').set({
init: 'init'
});
await this.dataService.storage.set(docId, timestamp);
}
In the above code, the .update() is the method that takes forever. Also other functions with the .add() method adding documents to a collection takes forever.
Again THESE METHODS ARE FAST ON WEB BROWSERS AND EMULATORS. Just not in the mobile app.
===========================================================================
NEW UPDATE: So it appears that the problem is actually in waiting for the Promise to return. I rewrote all of the functions used to no longer use add() or update(), but rather used set() after making a new document with doc() to replace add(). I then used set({...},{merge: true}) to replace update().
This time around the changes to the server were instant, but the problem came when waiting for the methods to return a promise from the server. This is the part that is causing the lag now. Does anyone know why this is occurring? I could simply change my code to not wait for these promises to return, but I would like to keep await within my code without having this issue.
One of my QA engineers is supporting an app with a fairly large codebase and a lot of different SharedPreferences files. He came to me the other day asking how to reset the application state between test runs, as if it had been uninstalled-reinstalled.
It doesn't look like that's supported by Espresso (which he is using) nor by the Android test framework natively, so I'm not sure what to tell him. Having a native method to clear all the different SharedPreferences files would be a pretty brittle solution.
How can one reset the application state during instrumentation?
Current espresso doesn't provide any mechanism to reset application state. But for each aspect (pref, db, files, permissions) exist a solution.
Initial you must avoid that espresso starts your activity automatically so you have enough time to reset.
#Rule
public ActivityTestRule<Activity> activityTestRule = new ActivityTestRule<>(Activity.class, false, false);
And later start your activity with
activityTestRule.launchActivity(null)
For reseting preferences you can use following snippet (before starting your activity)
File root = InstrumentationRegistry.getTargetContext().getFilesDir().getParentFile();
String[] sharedPreferencesFileNames = new File(root, "shared_prefs").list();
for (String fileName : sharedPreferencesFileNames) {
InstrumentationRegistry.getTargetContext().getSharedPreferences(fileName.replace(".xml", ""), Context.MODE_PRIVATE).edit().clear().commit();
}
You can reset preferences after starting your activity too. But then the activity may have already read the preferences.
Your application class is only started once and already started before you can reset preferences.
I have started to write an library which should make testing more simple with espresso and uiautomator. This includes tooling for reseting application data. https://github.com/nenick/espresso-macchiato See for example EspAppDataTool with the methods for clearing preferences, databases, cached files and stored files.
Improving on #nenick's solution, encapsulate the state clearing behavior in a custom ActivityTestRule. If you do this, you can allow the test to continue to launch the activity automatically without intervention from you. With a custom ActivityTestRule, the activity is already in the desired state when it launches for the test.
Rules are particularly useful because they're not tied to any specific test class, so can be easily reused within any test class or any project.
Below is one I implemented to ensure that the app is signed out when the activity launches, per test. Some tests, when they failed, were leaving the app in a signed in state. This would then cause later tests to also fail because the later ones assumed they would need to sign in, but the app would already be signed in.
public class SignedOutActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
public SignedOutActivityTestRule(Class<T> activityClass) {
super(activityClass);
}
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
InstrumentationRegistry.getTargetContext()
.getSharedPreferences(
Authentication.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE)
.edit()
.remove(Authentication.KEY_SECRET)
.remove(Authentication.KEY_USER_ID)
.apply();
}
}
you can try add this to gradle:
android {
...
defaultConfig {
...
testInstrumentationRunnerArguments clearPackageData: 'true'
}
}
refer to https://developer.android.com/training/testing/junit-runner
To remove all shared state from your device's CPU and memory after each test, use the clearPackageData flag.
I get a strange crash when using the new PageCurl effect reading a PDF with MonoTouch and iOS 5.0.
I've made a simple test case project for MonoDevelop 2.8 and uploaded on GitHub here:
https://github.com/Emasoft/IpaziaPDFReader
It seems that something is getting GCd too early and killing the application, but I can't find what. I've tried to dispose everything in many ways, but in vain. I've already submitted the project tarball to the Xamarin team, but they weren't able to solve the problem.
Is there something broken in the iOS NavigationController memory management? Or am I missing something?
Any help is appreciated, thanks!
UPDATE: I've tried to remove all subviews and sublayers before disposing the objects in all classes, but it still crashing. The only way I found to avoid the crash is to NEVER dispose of the PDF pages, adding them to a List before releasing them, but this is not a viable solution, because in that way memory is consumed rapidly for PDF with many pages and the app crashes anyway when unable to allocate memory for the next page.
Another way to avoid the crashes is to dispose of the PDF pages BEFORE turning the pages, forcing the dispose method on the page controller before creating a new page controller, but in this way the current page will become blank and the transition curls an useless empty page. No solution seems to work.
I've updated project on GitHub with the 3 different solutions I've tried (look in the PageDataSource class), you can uncomment them one at time to see the problems.
//SOLUTION 1
void ForcingPageControllerDispose (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGE IN THE TRANSITION IS BLANK, SO IS NOT VIABLE
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
}
//SOLUTION 2
void DisposeThePageControllerWhenDidFinishAnimating (BookPageController oldPageController, UIPageViewController pageViewController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES STILL HAPPEN
pageViewController.DidFinishAnimating += delegate(object sender, UIPageViewFinishedAnimationEventArgs e) {
if (currentPageController != null) {
currentPageController.View.RemoveFromSuperview ();
currentPageController.Dispose ();
Console.WriteLine ("currentPageController disposed for page: " + currentPageController.PageIndex);
}
};
}
//SOLUTION 3
void BackupUnusedPagesToAvoidBeingGCd (BookPageController oldPageController)
{
// --- IF YOU UNCOMMENT THIS, THE CRASHES GO AWAY, BUT THE PAGES ARE NOT GARBAGE COLLECTED AND AFTER MANY PAGES IPHONE IS OUT OF MEMORY AND IT CRASHES THE APP
if (parentController.book_page_controllers_reference_list.Contains (currentPageController) == false)
parentController.book_page_controllers_reference_list.Add (currentPageController);
}
I've already submitted the project tarball to the Xamarin team, but they weren't able to solve the problem.
I'm pretty sure the person assigned to your case will come up with the solution. The bigger the test case the more time it can take.
From a quick view the following, in your AppDelegate.cs, is wrong:
PageTurnViewController viewController = new PageTurnViewController ("PageTurnViewController", null);
window.AddSubview (viewController.View);
since the local viewController instance won't have any reference to it once FinishedLaunching returns and the GC will be able to collect it. However it's needed (on the native side) for keep the View fully valid. This can lead to crashes (there could be other cases too, that's the first and only file I checked this morning).
The solution is to promote the viewController to a field. That will make it alive even when the method returns, making it unavailable to collection.
UPDATE
I had a quick look at your code on github.
You are adding (sub)view but you never remove them (when the GC will Dispose them it won't remove them from the super view);
You are losing references to views, e.g. in PageDataSource.cs
newPageController = new BookPageController (nextPageIndex, parentController.currentPDFdocument, parentController);
return newPageController;
After the first page there will already be a reference stored in newPageController which will be overwritten and make the object collectable bug the GC. Since (sub)views are never removed there could still be native reference to them leading to crashes.
For debugging you can add your own finalizers, e.g.
~BookPageController ()
{
Console.WriteLine ("bu-bye");
}
and put breakpoints in them. If they get hit, while you think it's still in use, then you likely found a problem.
Since you are taking a dependency on iOS 5 new features, you should also adopt the new View Controller Containment APIs in iOS 5 that solve a few problems with view controllers.
I suggest you check the WWDC Video and slides for for Session 102 "Implement UIViewController Containment".
FATAL ERROR: CALL_AND_RETRY_2 Allocation Failed - process out of memory
I'm seeing this error and not quite sure where it's coming from. The project I'm working on has this basic workflow:
Receive XML post from another source
Parse the XML using xml2js
Extract the required information from the newly created JSON object and create a new object.
Send that object to connected clients (using socket.io)
Node Modules in use are:
xml2js
socket.io
choreographer
mysql
When I receive an XML packet the first thing I do is write it to a log.txt file in the event that something needs to be reviewed later. I first fs.readFile to get the current contents, then write the new contents + the old. The log.txt file was probably around 2400KB around last crash, but upon restarting the server it's working fine again so I don't believe this to be the issue.
I don't see a packet in the log right before the crash happened, so I'm not sure what's causing the crash... No new clients connected, no messages were being sent... nothing was being parsed.
Edit
Seeing as node is running constantly should I be using delete <object> after every object I'm using serves its purpose, such as var now = new Date() which I use to compare to things that happen in the past. Or, result object from step 3 after I've passed it to the callback?
Edit 2
I am keeping a master object in the event that a new client connects, they need to see past messages, objects are deleted though, they don't stay for the life of the server, just until their completed on client side. Currently, I'm doing something like this
function parsingFunction(callback) {
//Construct Object
callback(theConstructedObject);
}
parsingFunction(function (data) {
masterObject[someIdentifier] = data;
});
Edit 3
As another step for troubleshooting I dumped the process.memoryUsage().heapUsed right before the parser starts at the parser.on('end', function() {..}); and parsed several xml packets. The highest heap used was around 10-12 MB throughout the test, although during normal conditions the program rests at about 4-5 MB. I don't think this is particularly a deal breaker, but may help in finding the issue.
Perhaps you are accidentally closing on objects recursively. A crazy example:
function f() {
var shouldBeDeleted = function(x) { return x }
return function g() { return shouldBeDeleted(shouldBeDeleted) }
}
To find what is happening fire up node-inspector and set a break point just before the suspected out of memory error. Then click on "Closure" (below Scope Variables near the right border). Perhaps if you click around something will click and you realize what happens.