Know at runtime that you are running in IBDesignable - ios

(programming in swift 2)
In my iOS application I am using frameworks. I am also using the IBDesignable feature with great success. I have a bunch of views and buttons in frameworks that are IBDesignable and thus render nicely on the screen of interface builder.
Problem is that one of these views executes code when its initialized that will do an assertion if its being run just in the context of the IBDesignable interfacebuilder (executed just to render the IB screen). The reason for the assertion is valid, and really does not matter, bottom line is that I do not want to loose this functionality during "normal" operation.
My idea for a solution is to know when the code is being executed just for rendering in IB for IBDesignable mode, and thus build in a switch to not/do the assert.
I tried using the #if !TARGET_INTERFACE_BUILDER but this is not working, as this is a preprocessor directive and thus only evaluated a compile time, and the frameworks are precompiled (when used in the actual app).
I also thought about using prepareForInterfaceBuilder() but this is not applicable because the class throwing the assert has nothing todo with UIView itself.
Is there a function or any other way to check AT RUNTIME (in a framework) that your code is being run as part of IB screen rendering for IBDesignable mode?

After testing a few dozens solutions, I found the following to work reliably:
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = NSBundle.mainBundle().bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.rangeOfString("com.apple.") != nil
}

SWIFT 4 version
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = Bundle.main.bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.contains("com.apple.")
}

Related

Disable RealityKit/ARKit when building in xcode Simulator

Is it possible to disable RealityKit/ARKit/AR when building for xcode Simulator? So that I can style/change non-ar related pages.
I want to change my home page, adding different sections and styling. The home page has nothing to do with ARKit/Reality Kit, but I don't have the physical device with me.
The errors say that various RealityKit/ARKit functions are undefined or don't exist. Like ARView has no member .occlusion.
The home page has no need for AR at all, is it possible to build and run the simulator, or even preview without a physical (AR Enabled) device?
You can use a Conditional Compilation Block, as explained in the Swift Programming Manual:
class SubclassedARView : ARView {
var configuration: ARWorldTrackingConfiguration()
var runOptions: ARSession.RunOptions()
init() {
#if !targetEnvironment(simulator)
// Code here is only compiled when building to a real or generic device.
// This is the place where you should add your RealityKit specific
// code, or at least the code that gives build-time errors in Xcode.
// An example of a problematic method:
session.run(configuration, options: runOptions)
#endif
}
}

How do you check if SwiftUI is in preview mode?

Is there a way to check if a SwiftUI app is in preview mode? For example, you can check if your app is in development or production mode using #if DEBUG. Can you do something similar to check if you're previewing or not?
You can detect this using ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]. The value will be "1" at runtime when running in the canvas.
If you like me were looking for an environment variable to use in build scripts which xcode sets when building for SwiftUI previews, it turned out to be ENABLE_PREVIEWS.
SwiftUI were pausing preview when my script updated Info.plist file. To fix that I exit the script at certain point if we are in preview build.
if [ "${ENABLE_PREVIEWS}" = "YES" ]; then
exit 0;
fi
Though there is no compilation flag currently available for checking if the active build is intended for the Previews Canvas, I would still recommend using a compiler directive over a runtime check, if it can meet your needs.
For example, this check resolves to true for both the simulator and Previews:
#if targetEnvironment(simulator)
// Execute code only intended for the simulator or Previews
#endif
Negate the condition if you want your code to only execute on physical devices (such as camera-related operations that are otherwise guaranteed to fail).
The runtime check for whether your code is executing for Previews (as given in the accepted answer) probably does not add significant performance overhead, but it still feels a little gross to ship that code IMO. So it's worth at least considering first if your situation requires that level of specificity. If it does, I'd recommend wrapping that code in a compiler check to remove it from release builds.
If you don't want to rely on the ProccessInfo value you can always design your own environment variable in SwiftUI.
import SwiftUI
private struct IsPreviewKey: EnvironmentKey {
static let defaultValue = false
}
extension EnvironmentValues {
var isPreview: Bool {
get { self[IsPreviewKey.self] }
set { self[IsPreviewKey.self] = newValue }
}
}
Then when you create your preview inject the variable
MyView().environment(\.isPreview, true)
and you can use it in your view like this:
struct MyView: View {
#Environment(\.isPreview) var isPreview
}
I usually have a method that generates all the various versions for a preview (light mode, dark mode, iPad, iPhone, …) so I inject it there to all of the previews.

Storyboard, does not recognize UIControl actions anymore

This project runs and compiles fine, but in all storyboards if I was on a UIControl (such as a UIButton) interface builder doesn't seems able to detect it as the correct class.
As you can see in the connection inspector the action. The connection is spotted with an exclamation mark.
The identity inspector shows the class correctly.
I already tried to fully clean the project, even derived data.
Project build and runs fine but I can't connect actions anymore.
Any suggestion?
Apparently this seems a bug in Xcode.
To reproduce it you just need to make a protocol and make a UIButton conform to it.
This test few lines, breaks everything and you are no longer able to create and make connections from IB and the source file:
protocol TestProcol {
var test: String { get set }
}
extension UIButton: TestProcol {
var test: String {
set(val) {
titleLabel?.text = val
}
get {
return (titleLabel?.text!)!
}
}
}

How to detect if iOS app is running in UI Testing mode

I would like my app to run special code (e.g. resetting its state) when running in UI Testing mode. I looked at environment variables that are set when the app is running from UI Testing and there aren't any obvious parameters to differentiate between the app running normally vs in UI Testing. Is there a way to find out?
Two workarounds that I'm not satisfied with are:
Set XCUIApplication.launchEnvironment with some variable that I later check in the app. This isn't good because you have to set it in the setUp method of each test file. I tried setting the environment variable from the scheme settings but that doesn't propagate to the app itself when running UI Testing tests.
Check for the lack of existence of the environment variable __XPC_DYLD_LIBRARY_PATH. This seems very hacky and might only be working now because of a coincidence in how we have our target build settings set up.
I've been researching this myself and came across this question. I ended up going with #LironYahdav's first workaround:
In your UI test:
- (void)setUp
{
[super setUp];
XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchEnvironment = #{#"isUITest": #YES};
[app launch];
}
In your app:
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
if (environment[#"isUITest"]) {
// Running in a UI test
}
#JoeMasilotti's solutions are useful for unit tests, because they share the same runtime as the app being tested, but are not relevant for UI tests.
I didn't succeed with setting a launch environment, but got it to work with launch arguments.
In your tests setUp() function add:
let app = XCUIApplication()
app.launchArguments = ["testMode"]
app.launch()
In your production code add a check like:
let testMode = NSProcessInfo.processInfo().arguments.contains("testMode")
if testMode {
// Do stuff
}
Verified using Xcode 7.1.1.
You can use Preprocessor Macros for this. I found that you have couple of choices:
New Target
Make a copy of the App's target and use this as the Target to be Tested. Any preproocessor macro in this target copy is accessible in code.
One drawback is you will have to add new classes / resources to the copy target as well and sometimes it very easy to forget.
New Build Configuration
Make a duplicate of the Debug build configuration , set any preprocessor macro to this configuration and use it for your test (See screenshots below).
A minor gotcha: whenever you want to record a UI Testing session you need to change the Run to use the new testing configuration.
Add a duplicate configuration:
Use it for your Test:
Swift 3 based on previous answers.
class YourApplicationUITests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
let app = XCUIApplication()
app.launchArguments = ["testMode"]
app.launch()
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
}
extension UIApplication {
public static var isRunningTest: Bool {
return ProcessInfo().arguments.contains("testMode")
}
}
Then just call UIApplication.isRunningTest in your code.
I've just added this extension
#available(iOS 9, *)
extension XCUIApplication {
func test(){
launchEnvironment = ["TEST":"true"]
launch()
}
}
So I can just use test() instead of launch()
In Swift 3 you can check for the XCInjectBundleInto key, or something that starts with XC.
let isInTestMode = ProcessInfo.processInfo.environment["XCInjectBundleInto"] != nil
This works in OS X as well.
My solution is almost identical to that of Ciryon above, except that for my macOS Document-based app I had to prepend a hyphen to the argument name:
let app = XCUIApplication()
app.launchArguments.append("-Testing")
app.launch()
...otherwise, "Testing" ends up interpreted as the name of the document to open when launching the app, so I was getting an error alert:

Check if the running code is unit test case or not

I want to check if the running code is unit tese case or not execute different code for the result such as:
if ( unit test case is running )
{
do something
}
else
{
do other thing
}
any idea on this?
This is a bad approach you should try to simulate logic parts which you are trying to avoid by this statemetn through Mock ojects or other mechanism.
Now to your question you can use a oolean variable like isUnittest which you set on test setup and Teardown, ut as saied i dont recommend you doing this.
This seems to work for me (iOS 8, Xcode 6):
- (BOOL) isRunningTest {
return NSClassFromString(#"XCTestCase") != nil;
}
I think this is cleaner and easier than other answers.
Another way is to let the class have customizable behavior controlled via a static method, and have test case call that method in its static load method.
I had a similar issue using storyboard and an external restful service for authentication via oauth. The app delegate would check if there is a valid oauth token in appdelegate:didFinishLaunchingWithOptions, and if not, then programatically trigger segue to do the oauth login. But this was not desirable in test cases. To solve this, I created a static method within the app delegate to disable the login screen. This is the code within my app delegate:
static Boolean showLoginScreen = TRUE ;
+ (void) disableLoginScreen
{
showLoginScreen = FALSE ;
NSLog(#"disabled login screen") ;
}
The test case had its load method to do the following:
// disable login screen for the test case
+ (void) load {
NSLog( #"now disabling login screen" ) ;
[XYZAppDelegate disableLoginScreen];
}
This worked because the test case class was loaded before application was initialized. Of course you must check the value of this flag within app delegate to trigger/not trigger the login segue. Other alternatives I tried but rejected were as follows:
Create a preprocessor define on the test target. But the compiler only compiles the test case files with this flag, not the application source. See http://www.innovaptor.com/blog/2013/09/02/xcode-preprocessor-macros-for-test-code.html
Use the static initialize method of the test case to call the disable method. On test case run, the application is started before the test case class is loaded. See http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/ for some details.
Don't message UIAlertView directly. Instead, use dependency injection, for example, a property like
#property (strong, nonatomic) Class alertViewClass;
Then your code to create an alert can do
UIAlertView *alert = [[_alertViewClass alloc] initWithTitle:…etc…];
In your test code, inject a different class. I use https://github.com/jonreid/JMRTestTools to specify JMRMockAlertView. I can then test the alert calls with JMRMockAlertViewVerifier. (In fact, this enables test-driven development of alerts.)
Edit: These days, I use https://github.com/jonreid/ViewControllerPresentationSpy

Resources