Disable RealityKit/ARKit when building in xcode Simulator - ios

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
}
}

Related

Trying to use Naxam.Mapbox.iOS show blank page with Mapbox icon but no map

I've been trying to get the example going for Naxam.Mapbox.iOS as described here: https://blogs.naxam.net/using-mapbox-in-xamarin-ios-ffa9bdee13f4
I'm using Visual Studio/Xamarin 8.6.5 on my MacBook Pro with MacOS Catalina 10.15.4. I added NuGet Packages Naxam.Mapbox.iOS 5.4.0 and Xamarin.Essentials 1.5.3.2 to my project. I created an account at Mapbox.com and an access token for me to use in the project which I added as the MGLAccountManager.AccessToken property to the Info.plist file. The project has been created using the iOS Single View App template. Finally I added the --registrar:static argument to the Additional mtouch arguments in my iOS Build.
I added the first snippet of the blog post to my project as follows:
namespace MapboxSingleView
{
public partial class ViewController : UIViewController
{
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var map = new MGLMapView(View.Bounds, new NSUrl("mapbox://styles/naxamtest/cj5kin5x21li42soxdx3mb1yt"));
View.AddSubview(map);
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
}
}
}
Trying to run this in my simulator using Debug -> iPhone 11 iOS 13.5 shows a blank/white page with just the Mapbox logo in the bottom left corner but no actual map being displayed at all.
Trying without the NSUrl or adding the map's center & zoom level snippet as described doesn't help either. I tried contacting Naxam, but so far haven't received any response from them.
What did I miss?
The fact you can see the Mapbox logo indicates that you have done the installation correctly. The blank white page is usually down to a problem with the access token. You say you added
MGLAccountManager.AccessToken
to your info.plist file. The gif in the link on the tutorial page shows that if you are adding the entry in the info.plist file, then you should just add:
MGLAccountManager (with a value of <Your Mapbox access token>)
to that file. If you wish, instead, to add the access code in your code, then use:
MGLAccountManager.AccessToken = "<Your Mapbox access token>"
So it changes depending on if you want to enter your code in the info.plist file or as a line in your own code.

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.

Is Process always unresolved in iOS app or Playground?

In a iOS app, I want to use a file which have an following function using Process:
public func system(_ body: String) throws {
if #available(macOS 10.0, *) {
let process = Process()
...
} else {
fatalError()
}
}
Then, I got a fallowing error even though I applied Availability Condition and I don't evoke this function:
Use of unresolved identifier 'Process'.
I tried a similar code in Playground, and I got the same error.
I learned we cannot use Process in iOS Apps with a regular way by this question: How to execute terminal commands in Swift 4? , and I have a solution that I separate these codes with files by each using platforms. But I want to use this single file if I can.
Please give me your another solution for my ideal.
if #available() does a runtime check for OS versions.
if #available(macOS 10.0, *)
evaluates to true if the code is running on macOS 10.0 or later,
or on iOS/tvOS/watchOS with an OS which is at least the minimum deployment target.
What you want is a conditional compilation, depending on the platform:
#if os(macOS)
let process = Process()
#else
// ...
#endif
Even though you already solved this problem, just for you to know, I want to tell you that actually, Process() (or CommandLine() in Swift 3.0 or newer) is available for iOS, but you'll need to use a custom Objective-C header file which creates the object Process()/CommandLine(), or rather NSTask(), and everything it needs.
Then, in order to use this code with Swift, you'll need to create a Bridging-Header, in which you'll need to import the NSTask.h file for it to be exposed to Swift and being able to use it in your Swift code.
Once done this, use NSTask() instead of Process():
let process = NSTask() /* or NSTask.init() */
Or just use the following function in your code whenever you want to run a task:
func task(launchPath: String, arguments: String...) -> NSString {
let task = NSTask.init()
task?.setLaunchPath(launchPath)
task?.arguments = arguments
// Create a Pipe and make the task
// put all the output there
let pipe = Pipe()
task?.standardOutput = pipe
// Launch the task
task?.launch()
task?.waitUntilExit()
// Get the data
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
return output!
}
As you can see, NSTask() would be the equivalent to Process() in this case.
And call it like this:
task(launchPath: "/usr/bin/echo", arguments: "Hello World")
This will also return the value, so you can even display it by doing:
print(task(launchPath: "/usr/bin/echo", arguments: "Hello, World!"))
Which will print:
~> Hello, World!
For this to work and not throwing an NSInternalInconsistencyException, you'll need to set the launchPath to the executable's full path instead to just the directory containing it.
You'll also need to set all the command arguments separated by commas.
Tested on both iPad Mini 2 (iOS 12.1 ~> Jailbroken) and iPhone Xr (iOS 12.2 ~> not jailbroken).
NOTE: Even though this works both on non-jailbroken and jailbroken devices, your App will be rejected on the AppStore, as #ClausJørgensen said:
You're using private APIs, so it'll be rejected on the App Store. Also, Xcode 11 has some new functionality that will trigger a build failure when using certain private APIs.
If your app is targeting jailbroken iOS devices and will be uploaded to a third-party store like Cydia, Zebra, Thunderbolt or Sileo, then this would work correctly.
Hope this helps you.

dismissGrantingAccessToURL: failing with "didPickDocumentURLs called with nil or 0 URLS"

I have an iOS application that is providing Document Picker feature working perfectly on iOS 10 but that on iOS 11 always calls the documentPickerWasCancelled: with this message in logs:
[UIDocumentLog] UIDocumentPickerViewController : didPickDocumentURLs
called with nil or 0 URLS
I'm correctly calling dismissGrantingAccessToURL: with a valid NSURL on the provider extension but it never calls the documentPicker:didPickDocumentsAtURLs: on the other side.
I think I'm missing something, can you give me an explanation for this bad behaviour?
I'm having the same problems. Unfortunately I think the explanation is a bug or backwards-incompatibility in iOS 11. According to the documents it should be enough with a Document Picker extension:
"The Document Picker View Controller extension can perform import and export operations on its own. If you want to support open and move operations, you must pair it with a File Provider extension."
https://developer.apple.com/documentation/uikit/uidocumentpickerextensionviewcontroller?language=objc
And indeed this worked fine in iOS 10 and earlier. iOS 11 was probably meant to be backwards compatible with the existing FileProvider-less DocumentPickers, but seems it's not. Or perhaps they forgot to update the documents.
Instead, one can implement the new updated File Provider that gives access to your files via the standard document browser UI:
https://developer.apple.com/documentation/fileprovider
This does work with an iOS 11 FileProvider backing the iOS10 picker. You probably want to create a new FileProvider using the new Xcode template, then use :
#available(iOSApplicationExtension 11.0, *)
on the FileProviderItem and FileProviderEnumerator classes, then :
if #available(iOSApplicationExtension 11.0, *) {
in the methods on your FileProviderExtension
I find that my iOS 10 picker does correctly call this method, but note the completionHandler?(nil) was required to make it work. By default, the template for iOS11 inserts a completion that reports a failure. This code works for me:
override func startProvidingItem(at url: URL, completionHandler: ((_ error: Error?) -> Void)?) {
completionHandler?(nil)
// completionHandler?(NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
}
However, that isn't the end to this iOS10/11 incompatibility. If you make an iOS10/11 compatible file provider, it won't run on some iOS10 devices as far as I can see. I can run or debug mine on a 32-bit iOS device, but the FileProvider crashes on a 64-bit iOS 10 device with this error:
dyld: Library not loaded: /System/Library/Frameworks/FileProvider.framework/FileProvider
Referenced from: /private/var/containers/Bundle/Application/61BBD1A7-EA1E-4C10-A208-CA1DFA433C8D/test.app/PlugIns/testFileProvider.appex/testFileProvider
Reason: image not found

Know at runtime that you are running in IBDesignable

(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.")
}

Resources