I'm using XCode's XCTestCase for automated UI testing in order to measure performance of my application. I currently have a UITable with 25 000 elements in it and when trying to run tests that is supposed to swipe this list it takes for ever and crashes before finishing the test. The App targets CPU usage is at 100% at this point.
The last output in the console is:
Snapshot accessibility hierarchy for
When limiting the list down to a few hundred elements(not acceptable) the automated test is able to scroll the list at least but with around a 3-4 seconds wait between each scroll.
Test scenario:
let app = XCUIApplication();
app.buttons["Long list"].tap();
let table = app.tables.element;
table.swipeUp();
table.swipeUp();
So is there any way of speeding up the testing? Perhaps disabling the accessibility hierarchy(not using accessibility labels for the tests any ways).
maybe you can use api like - (double)pressAtPoint:(struct CGPoint)arg1 forDuration:(double)arg2 liftAtPoint:(struct CGPoint)arg3 velocity:(double)arg4 orientation:(long long)arg5 name:(id)arg6 handler:(CDUnknownBlockType)arg7;
#ifndef XCEventGenerator_h
#define XCEventGenerator_h
typedef void (^CDUnknownBlockType)(void);
#interface XCEventGenerator : NSObject
+ (id)sharedGenerator;
// iOS 10.3 specific
- (double)forcePressAtPoint:(struct CGPoint)arg1 orientation:(long long)arg2 handler:(CDUnknownBlockType)arg3;
- (double)pressAtPoint:(struct CGPoint)arg1 forDuration:(double)arg2 orientation:(long long)arg3 handler:(CDUnknownBlockType)arg4;
- (double)pressAtPoint:(struct CGPoint)arg1 forDuration:(double)arg2 liftAtPoint:(struct CGPoint)arg3 velocity:(double)arg4 orientation:(long long)arg5 name:(id)arg6 handler:(CDUnknownBlockType)arg7;
#end
#endif /* XCEventGenerator_h */
- (void)testExample {
XCUIApplication* app = [[XCUIApplication alloc] init];
XCUICoordinate* start_coord = [app coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.3)];
XCUICoordinate* end_coord = [app coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.7)];
NSLog(#"Start sleeping");
[NSThread sleepForTimeInterval:4];
NSLog(#"end sleeping");
for(int i = 0; i < 100; i++)
{
[[XCEventGenerator sharedGenerator] pressAtPoint:start_coord.screenPoint
forDuration:0
liftAtPoint:end_coord.screenPoint
velocity:1000
orientation:0
name:#"drag"
handler:^{}];
[NSThread sleepForTimeInterval:1];
}
}
Related
I am using a Bluno microcontroller to send / receive data from an iPhone, and everything is working as it should, but I would like to update the text of a UILabel with the real time data that is being printed from the Serial.print(numTicks); statement. If I stop the flowmeter the UILabel gets updated with the most current value, but I would like to update this label in realtime. I am not sure if this is a C / Arduino question or more of a iOS / Objective-C question. The sketch I'm loading on my Bluno looks like the following, https://github.com/ipatch/KegCop/blob/master/KegCop-Bluno-sketch.c
And the method in question inside that sketch looks like the following,
// flowmeter stuff
bool getFlow4() {
// call the countdown function for pouring beer
// Serial.println(flowmeterPin);
flowmeterPinState = digitalRead(flowmeterPin);
// Serial.println(flowmeterPinStatePinState);
volatile unsigned long currentMillis = millis();
// if the predefined interval has passed
if (millis() - lastmillis >= 250) { // Update every 1/4 second
// disconnect flow meter from interrupt
detachInterrupt(0); // Disable interrupt when calculating
// Serial.print("Ticks:");
Serial.print(numTicks);
// numTicks = 0; // Restart the counter.
lastmillis = millis(); // Update lastmillis
attachInterrupt(0, count, FALLING); // enable interrupt
}
if(numTicks >= 475 || valveClosed == 1) {
close_valve();
numTicks = 0; // Restart the counter.
valveClosed = 0;
return 0;
}
}
On the iOS / Objective-C side of things I'm doing the following,
- (void)didReceiveData:(NSData *)data Device:(DFBlunoDevice *)dev {
// setup label to update
_ticks = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_tickAmount setText:[NSString stringWithFormat:#"Ticks:%#",_ticks]];
[_tickAmount setNeedsDisplay];
NSLog(#"ticks = %#",_ticks);
}
Basically I would like to update the value of the UILabel while the flowmeter is working.
UPDATE
I just tested the functionality again with the serial monitor within the Arduino IDE, and I got the same if not similar results as to what I got via Xcode and the NSLog statements. So this leads me to believe something in the sketch is preventing the label from updating in real time. :/ Sorry for the confusion.
Problem:
After I fill an NSMutableArray with objects and try to do something with it, it contains (id)0x0 on some indices. I thought adding nil to an NSMutableArray wasn't possible at all in Objective-C so I am wondering why this happens.
When does this happen?
'Sometimes' unfortunately. It is reproducible by downloading more than ~5000 tiles, just to get the amount high enough for a chance for this to occur. Even with more than 5000 tiles it sometimes goes flawlessly.
Context:
My app has a button which starts a download for map tiles for a specific region. The download happens parallel in background threads and reports back for every tile downloaded.
To allow for canceling the download, in my downloader singleton I have a temporary NSMutableArray which saves a hash from every tile downloaded. After canceling, I can use that list of hashes to delete every saved tile in the database.
Saving the hashes during downloading seems to go fine, but when I actually want to do anything with it (I use [_currentTileHashes copy] to change it to an NSArray to give to the delete method), it throws an NSInvalidArgumentExceptionon that line saying:
-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3402]
When I use the debugger to inspect the _currentTileHashes mutable array, I indeed see that one or two of the indices is actually nil or (id)0x0. This screenshot illustrates it:
Relevant code:
This code is from the callback for every tile download where it hashes the tile, adds it to the hashes array and calls back to the UI for progress:
- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount {
DebugLog(#"Cached tile %lu of %lu.", (unsigned long)tileIndex, (unsigned long)totalTileCount);
if (_currentlyDownloading) {
float progress = (float)tileIndex / (float)totalTileCount;
NSDictionary *progressDict = #{#"progress" : [NSNumber numberWithFloat:progress],
#"routeId" : _downloadingRoute.routeId};
[_currentTileHashes addObject:[RMTileCache tileHash:tile]];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"routeTileDownloaded"
object:progressDict];
}
}
This is the way the tile gets hashed (this is from the Mapbox iOS SDK):
+ (NSNumber *)tileHash:(RMTile)tile
{
return [NSNumber numberWithUnsignedLongLong:RMTileKey(tile)];
}
uint64_t RMTileKey(RMTile tile)
{
uint64_t zoom = (uint64_t)tile.zoom & 0xFFLL; // 8bits, 256 levels
uint64_t x = (uint64_t)tile.x & 0xFFFFFFFLL; // 28 bits
uint64_t y = (uint64_t)tile.y & 0xFFFFFFFLL; // 28 bits
uint64_t key = (zoom << 56) | (x << 28) | (y << 0);
return key;
}
And finally, the code where the exception occurs:
- (void)tileCacheDidCancelBackgroundCache:(RMTileCache *)tileCache {
DebugLog(#"Finished canceling tile download");
[tileCache removeAllCachedImagesForTileHashes:[_currentTileHashes copy]];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"routeTileDownloadCanceled"
object:nil];
}
Tested on iOS 8.4, 8.4.1 (iPhone 6) and 7.1 (iPhone 4)
Feel free to ask for more clarification if something is unclear.
NSMutableArray is not thread safe, so updating an instance from multiple, concurrent, background downloads is likely to lead to corruption in your array - as you are seeing.
I would suggest using #synchronized to guard the array when you update it -
- (void)tileCache:(RMTileCache *)tileCache didBackgroundCacheTile:(RMTile)tile withIndex:(NSUInteger)tileIndex ofTotalTileCount:(NSUInteger)totalTileCount {
DebugLog(#"Cached tile %lu of %lu.", (unsigned long)tileIndex, (unsigned long)totalTileCount);
if (_currentlyDownloading) {
float progress = (float)tileIndex / (float)totalTileCount;
NSDictionary *progressDict = #{#"progress" : [NSNumber numberWithFloat:progress],
#"routeId" : _downloadingRoute.routeId};
#synchronized(_currentTileHashes) {
[_currentTileHashes addObject:[RMTileCache tileHash:tile]];
}
[[NSNotificationCenter defaultCenter]
postNotificationName:#"routeTileDownloaded"
object:progressDict];
}
}
When i try to use the ParseObject to store the local data, i find that the performance is very bad, when i execute below codes at IOS 5, will take about 1 - 2 secs for every loop. But seems that the performance is ok at emulator and iphone 5s.
for(int i = 0; i < 20; i++){
PFObject *pfObj = [PFObject objectWithClassName:#"TestStore"];
pfObj[#"TestStore_ID"] = userId;
pfObj[#"TestStore_URL"] = avatarUrl;
[pfObj pinWithName:#"TestStore"];
}
The pinWithName: method is synchronous and pinning executes SQLite statements under the hood, which can be quite slow. You should try using pinInBackgroundWithName: to execute this outside of the main thread.
I am sure this has already occured to someone but I could not find a trusted source (on Stackoverflow or elsewhere) to solve this.
I have to download a (finite) number of files. I don't know their size prior to downloading them. Their size can greatly change and I don't know it before beginning the download.
It can often happen that I have 9 really small files and 1 really big file.
If I use NSProgress with its "children" feature, I will very quickly show a completion of 90%, then the UI will appear to be stuck (even though it is not the case) since the last file is much bigger.
Here is an example code where I simulate a large file.
- (void)viewDidLoad
{
[super viewDidLoad];
nbTasks = 10;
mainProgress = [NSProgress progressWithTotalUnitCount:nbTasks];
[self launch];
}
- (void)launch {
for (int i = 0; i < nbTasks; ++i) {
[mainProgress becomeCurrentWithPendingUnitCount:1];
[self launchWithNumber:i];
[mainProgress resignCurrent];
}
}
- (void)launchWithNumber:(int)count {
int max = count == 0 ? 100 : 10;
NSProgress *localProgress = [NSProgress progressWithTotalUnitCount:max];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
for (int i = 0; i < max; ++i) {
localProgress.completedUnitCount++;
[NSThread sleepForTimeInterval:1];
}
});
}
How would you handle this case where you cannot change the becomeCurrentWithPendingUnitCount since you don't know its weight prior to begin downloading ?
EDIT: This is what I do for now:
First of all: I lied. I don't download files but data from a database, but it's the same problem in the end.
Before creating my root NSProgress, I download the number of objects from my backend (SELECT count(*) FROM ...). With this I can call becomeCurrentWithPendingUnitCount with the correct "weight" and the progress appears more "smooth".
Of course, this is far from ideal for me since I have to wait some time before showing the first NSProgress, and it makes me do more queries to the backend, but at least the experience is a bit better.
If I could remove this it would be much better. Any ideas?
I want to make live streaming for more then two users on different devices and get api from opentok i had download demo app from ( https://github.com/opentok/OpenTok-iOS-Hello-World) and this is not webrtc, i had run application with key, session and token with disables of pear to pear,
And its working fine for two live streaming but while i tray to connect third stream i am not able to getting that,
I found staring in demo app that (On iPad 2 / 3 / 4, the limit is four streams. An app can have up to four simultaneous subscribers, or one publisher and up to three subscribers.)
with this i am testing with three iPads and got just two on screen
so how to make this more then two stream at a time in three iPads
The project you linked (OpenTok-iOS-Hello-World) is built to just subscribe to one stream. Just as a proof of concept, you can get two subscribers on screen pretty simply by just modifying a few methods and adding an instance variable in ViewController.m
Create a variable that tracks the number of subscribers:
#implementation ViewController {
OTSession* _session;
OTPublisher* _publisher;
OTSubscriber* _subscriber;
int _numSubscribers; // **NEW**
}
Initialize the variable in the initialization method:
- (void)viewDidLoad
{
[super viewDidLoad];
_session = [[OTSession alloc] initWithSessionId:kSessionId
delegate:self];
_numSubscribers = 0; // **NEW**
[self doConnect];
}
Make sure we aren't subscribing to our own stream:
static bool subscribeToSelf = NO;
Modify stop caring about whether there is already a subscriber in this session delegate method:
- (void)session:(OTSession*)mySession didReceiveStream:(OTStream*)stream
{
NSLog(#"session didReceiveStream (%#)", stream.streamId);
// See the declaration of subscribeToSelf above.
if ( (subscribeToSelf && [stream.connection.connectionId isEqualToString: _session.connection.connectionId])
||
(!subscribeToSelf && ![stream.connection.connectionId isEqualToString: _session.connection.connectionId])
) {
// ** Changing if statement **
if (_numSubscribers < 2) {
_subscriber = [[OTSubscriber alloc] initWithStream:stream delegate:self];
_numSubscribers++;
}
}
}
Place the subscribers next to one another, taking up a little less width:
- (void)subscriberDidConnectToStream:(OTSubscriber*)subscriber
{
NSLog(#"subscriberDidConnectToStream (%#)", subscriber.stream.connection.connectionId);
// ** Calculate the frame **
CGRect subFrame = CGRectMake(0, widgetHeight, widgetWidth / 2, widgetHeight)
if (_numSubscribers == 2) subFrame = CGRectOffset(subFrame, widgetWidth / 2, 0);
[subscriber.view setFrame:subFrame];
[self.view addSubview:subscriber.view];
}
NOTE: This solution doesn't result in a stable App. It should get you to a point where you can see both subscribers as long as you don't disconnect any of the iPads in between. To finish this off, you will need to store the OTSubscribers created in session:didRecieveStream: in a collection like NSArray, handle removing the right subscriber(s) and decrementing the _numSubscribers in session:didDropStream:, and thinking about how you want the updateSubscriber method to work instead.
If you look at the source code for hello world in the viewcontroller file line 93, you will see that it is only creating one subscriber. To have multiple subscribers simple create an array or hash object to store multiple subscribers.