The appDelegate instance is showing nil value, test case "testAppDelegate" is getting failed.
The same is working in the sample code provided by apple developer site but there SenTestCase is being used, please help me out, even the target is set as per the WWDC 2013 video "Testing in Xcode 5 session 409"
#interface Tests : XCTestCase
{
AppDelegate *appDelegate;
AppViewController *appVC;
UIView *appView;
}
#end
#implementation Tests
- (void)setUp
{
[super setUp];
appDelegate = [[UIApplication sharedApplication] delegate];
appVC = appDelegate.appViewController;
appView = appVC.view;
}
- (void)tearDown
{
[super tearDown];
}
- (void)testAppDelegate
{
XCTAssert(appDelegate, #"Cannot find the application delegate");
}
- (void)testCheckForViewInitializatio
{
XCTAssert(appVC, #"AppViewController initialized");
}
Try this:
go to Project Settings
select your test target
in General tab switch Target section to your main target
Run again
Anton gives right suggestion.
But if it doesn't works (as in my case) try to:
Remove your existing Test Target
Create new Test Target (5-th tab in left pane -> click on + in bottom left corner -> New Test Target) and
In appeared window don't forget to choose your application as target of your test target.
Add all your TestCases files to new target.
I don't know the reason, but after this action it start working correctly, when I run tests with new target.
Related
If the app is installed at the first time, need to allow notification, how can I confirm it? Is someone encountered?
you should typically be mocking notifications and other data requests in order to prevent the dialogs from coming up. You could also accept the notification manually and re-run your tests. We experimented with using the private UIAutomation framework for this and saw we could achieve this with it. For example, for pressing the left alert button.
#interface SystemAlert : NSObject
- (void)tapLeftButton;
#end
#interface SystemAlert (ForMethodCompletionOnly)
+ (id)localTarget;
- (id)frontMostApp;
- (id)alert;
- (id)buttons;
#end
#implementation SystemAlert
+ (void)load {
dlopen([#"/Developer/Library/PrivateFrameworks/UIAutomation.framework/UIAutomation" fileSystemRepresentation], RTLD_LOCAL);
}
- (void)tapLeftButton {
id localTarget = [NSClassFromString(#"UIATarget") localTarget];
id app = [localTarget frontMostApp];
id alert = [app alert];
id button = [[alert buttons] objectAtIndex:0];
[button tap];
}
#end
I've run into an issue with this little Objective-C project I'm doing and it's proving to be a bit of a roadblock. I'm playing around with Apple's NSSpeechRecognizer software on El Capitan, and I'm trying to get this guy running properly so that when the riddle I give it is posed to the user, the user can respond with a word to "do something cool". As it stands right now, the delegate method:
-(void) speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command { ... }`
is never even called, even though it appears the recognition icon is correctly detecting the answer to the riddle.
The problem is that your main function has a loop that is continually checking whether the speech has been recognizing. You are not giving NSSpeechRecognizer a chance to actually deliver any messages to you.
Your app needs to let the main "run loop" run, so it can deliver messages. Normally, in an OS X app, your main would just call NSApplicationMain, which does this for you.
Your code is effectively this:
#interface RecognizerDelegate : NSObject <NSSpeechRecognizerDelegate>
#property (nonatomic) NSSpeechRecognizer *recognizer;
#property (nonatomic) BOOL didRecognize;
#end
#implementation RecognizerDelegate
- (id)init
{
if ((self = [super init])) {
self.didRecognize = NO;
self.recognizer = [[NSSpeechRecognizer alloc] init];
self.recognizer.listensInForegroundOnly = NO;
self.recognizer.blocksOtherRecognizers = YES;
self.recognizer.delegate = self;
self.recognizer.commands = #[ #"hello" ];
[self.recognizer startListening];
}
return self;
}
- (void)speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(NSString *)command
{
self.didRecognize = YES;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool
{
RecognizerDelegate *recognizerDelegate = [[RecognizerDelegate alloc] init];
while (recognizerDelegate.didRecognize == NO) {
// do nothing
}
NSLog(#"Recognized!");
}
return 0;
}
That while loop is doing nothing useful, just running your CPU in a loop and wasting time and energy. You are not letting any other code in NSSpeechSynthesizer, or any of the system frameworks like Foundation or AppKit, get the chance to do anything. So, nothing happens.
To fix this in the short term: you can let the main run loop run for a little while in each pass through the loop. This code would let the system run for a second, then would return to your code, so you could check again:
while (recognizerDelegate.didRecognize == NO) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
The longer-term fix would be to move your code out of main and to structure it like a real OS X app. Instead of using a loop to poll a condition like recognizerDelegate.didRecognize, you would just trigger the "next thing" directly from delegate methods like -speechRecognizer:didRecognizeCommand:, or you would use things like NSTimer to run code periodically.
For more details, see the Apple doc Cocoa Application Competencies for OS X, specifically the "Main Event Loop" section.
I had the same problem using NSSpeechRecognizer. The callback function:
func speechRecognizer(_ sender: NSSpeechRecognizer,
didRecognizeCommand command: String) {}
...was never called, even though everything appeared to be working.
There were three things I changed to get the code working.
1) I had to enable the entitlement in my "sandboxed" mode application to allow for microphone use.
... I also did these other two things, as well.
2) I added the "Privacy - Microphone Usage Description" in the info.pList, and set the string value to "I want to listen to you speak"
3) I added the "Privacy - Speech Recognition Usage Description" in the info.pList, and set the string value to "I want to write down what you say"
How can I generate code coverage with Xcode 5 and iOS7?
Prior to upgrading I was getting code coverage just fine. Now I can't see any *.gcda files being produced.
The cmd-line that I'm using is:
xcodebuild -workspace ${module.name}.xcworkspace test -scheme ${module.name} -destination OS=${module.sdk.version},name=iPad -configuration Debug
Works with AppCode
When I execute the tests via AppCode I can see *.gcda files being produced in ~/Library/Caches/appCode20/DerivedData. . . I need this to work for my Continuous Integration builds.
Works from Xcode IDE
Also works from Xcode IDE. . . is there a cmd-line that will produce coverage, or is this an Xcode bug?
The following is a fix for SenTestKit - simply add this class to your Tests target. Something similar should be possible to do with XCTest
#interface VATestObserver : SenTestLog
#end
static id mainSuite = nil;
#implementation VATestObserver
+ (void)initialize {
[[NSUserDefaults standardUserDefaults] setValue:#"VATestObserver" forKey:SenTestObserverClassKey];
[super initialize];
}
+ (void)testSuiteDidStart:(NSNotification*)notification {
[super testSuiteDidStart:notification];
SenTestSuiteRun* suite = notification.object;
if (mainSuite == nil) {
mainSuite = suite;
}
}
+ (void)testSuiteDidStop:(NSNotification*)notification {
[super testSuiteDidStop:notification];
SenTestSuiteRun* suite = notification.object;
if (mainSuite == suite) {
UIApplication* application = [UIApplication sharedApplication];
[application.delegate applicationWillTerminate:application];
}
}
and add
extern void __gcov_flush(void);
- (void)applicationWillTerminate:(UIApplication*)application {
__gcov_flush();
}
Why is this working?
Tests and the tested application are compiled separately. Tests are actually injected into the running application, so the __gcov_flush() must be called inside the application not inside the tests.
The little magic with the observer only enables us to check when the tests are going to end and we trigger __gcov_flush() to be called inside the app.
(This is not the answer, but a work-around . . .I'm still very much interested in a better solution)
Use iOS 6.1 Simulator
If you're targeting iOS 6.1 or earlier as a deployment target, you can use the 6.1 simulator.
Install the iOS6.1 Simulator via preferences/downloads
Use the following cmd-line:
xcodebuild -workspace ${module.name}.xcworkspace test -scheme ${module.name} -destination OS=6.1,name=iPad -configuration Debug
We found that we had to add a bit of code to get the gcda files to flush from the system.
Code addition is to add
extern void __gcov_flush(); to the top of your file and then call __gcov_flush(); just before the entire test suite exits.
Full explanation is here: http://www.bubblefoundry.com/blog/2013/09/generating-ios-code-coverage-reports/
With the information from here I was able to craft this version which is the least invasive I could think of. Just add to your unit tests and run the tests as normal. The ZZZ ensures it is the last run suite of tests.
I had to ensure I added the GCC_GENERATE_TEST_COVERAGE_FILES and GCC_GENERATE_TEST_COVERAGE_FILES compiler flags to my test unit target too to get the coverage out.
//
// Created by Michael May
//
#import <SenTestingKit/SenTestingKit.h>
#interface ZZZCodeCoverageFixForUnitTests : SenTestCase
#end
#implementation ZZZCodeCoverageFixForUnitTests
// This must run last
extern void __gcov_flush();
-(void)testThatIsntReallyATest
{
NSLog(#"FLUSHING GCOV FILES");
__gcov_flush();
}
#end
Edit, or another approach by Jasper:
I stripped the VATestObserver from the other answer down to this:
#interface VATestObserver : SenTestLog
#end
#implementation VATestObserver
extern void __gcov_flush(void);
- (void)applicationWillTerminate:(UIApplication*)application
{
__gcov_flush();
[super applicationWillTerminate:application];
}
#end
Some more documentation here:
https://code.google.com/p/coverstory/wiki/UsingCoverstory
and some source code to use:
https://code.google.com/p/google-toolbox-for-mac/source/browse/#svn%2Ftrunk%2FUnitTesting
You need GTMCodeCoverageApp.h/.m and GTMCodeCoverageTestsXC.h/.m or GTMCodeCoverageTestsST.h/.m depending on if you are using XCTest or SenTest.
Update: New accepted answer
In some cases the coverage flushing needs to be done from within the app itself. The solution's outline in this question provide details.
I keep getting this error when I try to build my project which uses reachability (the error only came up after I tried to implement reachability):
I read some other posts on the internet but nothing seemed to work. I added SystemConfiguration.framework (project, build phases, +), but that didn't work (I had already added it when the error came up). Here's how I implement the files:
#import <UIKit/UIKit.h>
#import "Reachability.h"
#interface catalogDetailView : UIViewController{
}
-(void)checkNetwork;
#end
Then in the .m:
#import "catalogDetailView.h"
-(void)checkNetwork{
if ([self reachable]) {
NSLog(#"Connected to Network");
}
else {
NSLog(#"Connection Failed");
}
}
-(BOOL)reachable {
Reachability *r = [Reachability reachabilityWithHostName:#"apple.com"];
NetworkStatus internetStatus = [r currentReachabilityStatus];
if(internetStatus == NotReachable) {
return NO;
}
return YES;
}
So if you could help that would be amazing!! Thanks ;)
Luke
You need to add Reachability.m to your target's "Compile Sources" build phase.
One way to do it:
Select Reachability.m in the project navigator, on the left side of the Xcode window. If you don't see it, choose the menu View > Navigators > Show Project Navigator.
Show the file inspector on the right side of the Xcode window. View > Utilities > Show File Inspector.
In the Target Membership section, check the checkbox next to your target.
I'll bet you that your 'compile sources list' does not contain reachability.m. Drag it in under the build phases tab to the list. I often find that when adding files to a project, Xcode often doesn't add the files your target's compile list (because the default is to not add them).
I am building a really basic Cocoa application using WebKit, to display a Flash/Silverlight application within it. Very basic, no intentions for it to be a browser itself.
So far I have been able to get it to open basic html links (<a href="..." />) in a new instance of Safari using
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
Now my difficulty is opening a link in a new instance of Safari when window.open() is used in JavaScript. I "think" (and by this, I have been hacking away at the code and am unsure if i actually did or not) I got this kind of working by setting the WebView's policyDelegate and implementing its
-webView:decidePolicyForNavigationAction:request:frame:decisionListener:
delegate method. However this led to some erratic behavior.
So the simple question, what do I need to do so that when window.open() is called, the link is opened in a new instance of Safari.
Thanks
Big point, I am normally a .NET developer, and have only been working with Cocoa/WebKit for a few days.
I made from progress last night and pinned down part of my problem.
I am already using webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: and I have gotten it to work with anchor tags, however the method never seems to get called when JavaScript is invoked.
However when window.open() is called webView:createWebViewWithRequest:request is called, I have tried to force the window to open in Safari here, however request is always null. So I can never read the URL out.
I have done some searching around, and this seems to be a known "misfeature" however I have not been able to find a way to work around it.
From what I understand createWebViewWithRequest gives you the ability to create the new webview, the the requested url is then sent to the new webView to be loaded. This is the best explanation I have been able to find so far.
So while many people have pointed out this problem, I have yet to see any solution which fits my needs. I will try to delve a little deeper into the decidePolicyForNewWindowAction again.
Thanks!
Well, I'm handling it by creating a dummy webView, setting it's frameLoad delegate to a custom class that handles
- (void)webView:decidePolicyForNavigationAction:actionInformation :request:frame:decisionListener:
and opens a new window there.
code :
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
//this is a hack because request URL is null here due to a bug in webkit
return [newWindowHandler webView];
}
and NewWindowHandler :
#implementation NewWindowHandler
-(NewWindowHandler*)initWithWebView:(WebView*)newWebView {
webView = newWebView;
[webView setUIDelegate:self];
[webView setPolicyDelegate:self];
[webView setResourceLoadDelegate:self];
return self;
}
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
}
-(WebView*)webView {
return webView;
}
There seems to be a bug with webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: in that the request is always nil, but there is a robust solution that works with both normal target="_blank" links as well as javascript ones.
Basically I use another ephemeral WebView to handle the new page load in. Similar to Yoni Shalom but with a little more syntactic sugar.
To use it first set a delegate object for your WebView, in this case I'm setting myself as the delegate:
webView.UIDelegate = self;
Then just implement the webView:createWebViewWithRequest: delegate method and use my block based API to do something when a new page is loaded, in this case I'm opening the page in an external browser:
-(WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
return [GBWebViewExternalLinkHandler riggedWebViewWithLoadHandler:^(NSURL *url) {
[[NSWorkspace sharedWorkspace] openURL:url];
}];
}
That's pretty much it. Here's the code for my class. Header:
// GBWebViewExternalLinkHandler.h
// TabApp2
//
// Created by Luka Mirosevic on 13/03/2013.
// Copyright (c) 2013 Goonbee. All rights reserved.
//
#import <Foundation/Foundation.h>
#class WebView;
typedef void(^NewWindowCallback)(NSURL *url);
#interface GBWebViewExternalLinkHandler : NSObject
+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler;
#end
Implemetation:
// GBWebViewExternalLinkHandler.m
// TabApp2
//
// Created by Luka Mirosevic on 13/03/2013.
// Copyright (c) 2013 Goonbee. All rights reserved.
//
#import "GBWebViewExternalLinkHandler.h"
#import <WebKit/WebKit.h>
#interface GBWebViewExternalLinkHandler ()
#property (strong, nonatomic) WebView *attachedWebView;
#property (strong, nonatomic) GBWebViewExternalLinkHandler *retainedSelf;
#property (copy, nonatomic) NewWindowCallback handler;
#end
#implementation GBWebViewExternalLinkHandler
-(id)init {
if (self = [super init]) {
//create a new webview with self as the policyDelegate, and keep a ref to it
self.attachedWebView = [WebView new];
self.attachedWebView.policyDelegate = self;
}
return self;
}
-(void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
//execute handler
if (self.handler) {
self.handler(actionInformation[WebActionOriginalURLKey]);
}
//our job is done so safe to unretain yourself
self.retainedSelf = nil;
}
+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler {
//create a new handler
GBWebViewExternalLinkHandler *newWindowHandler = [GBWebViewExternalLinkHandler new];
//store the block
newWindowHandler.handler = handler;
//retain yourself so that we persist until the webView:decidePolicyForNavigationAction:request:frame:decisionListener: method has been called
newWindowHandler.retainedSelf = newWindowHandler;
//return the attached webview
return newWindowHandler.attachedWebView;
}
#end
Licensed as Apache 2.
You don't mention what kind of erratic behaviour you are seeing. A quick possibility, is that when implementing the delegate method you forgot to tell the webview you are ignoring the click by calling the ignore method of the WebPolicyDecisionListener that was passed to your delegate, which may have put things into a weird state.
If that is not the issue, then how much control do you have over the content you are displaying? The policy delegate gives you easy mechanisms to filter all resource loads (as you have discovered), and all new window opens via webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:. All window.open calls should funnel through that, as will anything else that triggers a new window.
If there are other window opens you want to keep inside your app, you will to do a little more work. One of the arguments passed into the delegate is a dictionary containing information about the event. Insie that dictionary the WebActionElementKey will have a dictionary containing a number of details, including the original dom content of the link. If you want to poke around in there you can grab the actual DOM element, and check the text of the href to see if it starts with window.open. That is a bit heavy weight, but if you want fine grained control it will give it to you.
By reading all posts, i have come up with my simple solution, all funcs are in same class,here it is, opens a link with browser.
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
return [self externalWebView:sender];
}
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
{
[[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
}
-(WebView*)externalWebView:(WebView*)newWebView
{
WebView *webView = newWebView;
[webView setUIDelegate:self];
[webView setPolicyDelegate:self];
[webView setResourceLoadDelegate:self];
return webView;
}
Explanation:
Windows created from JavaScript via window.open go through createWebViewWithRequest.
All window.open calls result in a createWebViewWithRequest: with a null request, then later a location change on that WebView.
For further information, see this old post on the WebKit mailing list.
An alternative to returning a new WebView and waiting for its loadRequest: method to be called, I ended up overwriting the window.open function in the WebView's JSContext:
First, I set my controller to be the WebFrameLoadDelegate of the WebView:
myWebView.frameLoadDelegate = self;
Then, in the delegate method, I overwrote the window.open function, and I can process the URL there instead.
- (void)webView:(WebView *)webView didCreateJavaScriptContext:(JSContext *)context forFrame:(WebFrame *)frame{
context[#"window"][#"open"] = ^(id url){
NSLog(#"url to load: %#", url);
};
}
This let me handle the request however I needed to without the awkward need to create additional WebViews.