App not showing in external display extension for any iPhone - ios

I created a new app in Xcode and added the following code in the AppDelegate file
func updateCarWindow()
{
guard let screen = UIScreen.screens.first(where: { $0.traitCollection.userInterfaceIdiom == .carPlay })
else
{
// CarPlay is not connected
self.carWindow = nil;
return
}
// CarPlay is connected
let carWindow = UIWindow(frame: screen.bounds)
carWindow.screen = screen
carWindow.makeKeyAndVisible()
carWindow.rootViewController = CarViewController(nibName: nil, bundle: nil)
self.carWindow = carWindow
}
and called the function in function application. The app is not showing in the CarPlay external display.

You don’t have direct access to the carplay screen, carplay manages everything using the CPInterfaceController class that is able to display so called templates (such as CPListTemplate and a handful of others). Your ability to draw on the screen is pretty much limited to drawing maps in a CPMapContentWindow.
I recommend you read the Apple docs first starting here:
https://developer.apple.com/carplay/documentation/CarPlay-Navigation-App-Programming-Guide.pdf
Don’t forget to set the correct app permissions and carplay entitlements othereise it simply won’t work and it might not tell you why.
And a final word that the Carplay framework is only supposed to work with navigation apps. Everything else would require a lot of workarounds, not to mention it would never pass app review.
Hope this helps

Related

iOS 15 DualSense is opening App Library on PS button long press

On iOS 15 a long press on the PS Button of the DualSense controller is opening the App Library and I don't receive a callback via the valueChangedHandler function. The App library which will be opened looks like this
This is how I handle all controller inputs:
func handleController(controller: GCController) {
controller.extendedGamepad?.valueChangedHandler = { [weak self] (gamepad: GCExtendedGamepad, element: GCControllerElement) in
guard let self = self else {
return
}
// no feedback received when performing a long press on the PS button
}
Can the game library be suppressed somehow? Sony's PS Remote Play app somehow manages to suppress it, but I don't know how, nor can I find anything in Apple's official API documentation.
Edit: Seems this problem only occurs on iPads, on iPhones this problem doesn't exist. Is there some API or anything on iPads to suppress this behaviour? I assume the most majority of users don't want to open the App Library in the middle of the game.
If someone ever faces the same problem you can actually disable system gestures for the Home button.
In Swift all you have to add is this line (controller is a GCController object)
controller.physicalInputProfile.buttons[GCInputButtonHome]?.preferredSystemGestureState = .disabled
In ObjectiveC it would work like this
controller.physicalInputProfile.buttons[GCInputButtonHome].preferredSystemGestureState = GCSystemGestureStateDisabled;
Thanks to the Apple employee who helped me here
https://developer.apple.com/forums/thread/711905
Edit: on tvOS this isn't working as the PS button (menu button) of a controller always have to act as home event
https://developer.apple.com/forums/thread/715012

ReplayKit Broadcast upload extension - Service not found

I'm working on an IOS swift application that will allow the user to record the entire screen, any app and even the home screen.
In order to do that, I added a Broadcast Upload Extension to my app.
First I used the RPSystemBroadcastPickerView class in order to add a record button to my view that allow the user to open the record popup and select to which app he wants to broadcast the screen flow. And it's working fine :
But I would like to avoid this step and directly open the popup when the app launch.
So I wrote the following code to do that :
RPBroadcastActivityViewController.load(withPreferredExtension: "ch.jroueche.RecordApp.TestScreen", handler: {broadcastAVC,error in
guard error == nil else {
print("Cannot load Broadcast Activity View Controller.")
return
}
if let broadcastAVC = broadcastAVC {
broadcastAVC.delegate = self
self.present(broadcastAVC, animated: true, completion: {
// broadcastactivityviewcontroller will perform the callback when the broadcast starts (or fails)
print("I've START")
})
}
})
Unlikeenter code here the RPSystemBroadcastPickerView solution, I'm getting the following error :
The preferred broadcast service could not be found.
My issue is similar to the following post :
App not showing up as a broadcast service in RPBroadcastActivityViewController
I also added the extension and the preferred extension identifier is correct.
Why would it be possible using the RPSystemBroadcastPickerView and not programmatically using RPBroadcastActivityViewControllerclass. That does not make sense for me.
Does someone have an idea of what could be the issue and how could I fix it ? Or a workaround in order to do this screen record.
Thanks in advance
It appears that RPBroadcastActivityViewController shows ONLY Broadcast Setup UI Extension, while RPSystemBroadcastPickerView shows ONLY Broadcast Upload Extension. But I have no idea why, as both of them are stated to show list of available providers/services.
It would be very helpful if someone could bring more details on the topic.

App Clip - Support Multiple Businesses Locations

When I designed my App Clip Launch experience, I had in mind that the App can only be triggered via QR code, NFC or App Clip Code. That why I linked the App Launch to a specific location with specific Id.
When my App went live last week, and when I try to scan a NFC tag the App is launching as expected every time.
Now, if I tap the App Clip icon on the home screen, the App is launching with the last URL scanned I dig some googling and I found that the App Clip is caching the last URL scanned and simulating a universal link launch when icon tapped!
This is not working for me! So I am looking for a way to check if the App was launched via scan or tap? I tried to log the App launch but it's always running in the order either via Scan (NFC) or icon tap:
AppDelegate.didFinishLaunchingWithOptions()
SceneDelegate.willConnectTo() // It's here where I am handling the Universal Link
How can I check if the user launched the App via Tap or Scan? Knowing that the App is always simulating Universal launch Link when icon tapped!
Or how I can look for the saved URL? I tried to fetch all UserDefaults and Some Keychain data, but I found nothing!
I faced the same issue! And unfortunately there’s no way to:
Check how the App was launched, icon tap or NFC/QR scan
To retrieve cached data from either UserDefaults or Keychain
Apple says clearly on their Human Interface Guidelines that if you want support multiple businesses you should add the location services factor!
Consider multiple businesses. An App Clip may power many different
businesses or a business that has multiple locations. In both
scenarios, people may end up using the App Clip for more than one
business or location at a time. The App Clip must handle this use case
and update its user interface accordingly. For example, consider a way
to switch between recent businesses or locations within your App Clip,
and verify the user’s location when they launch it.
So, now your tags for specific location should be mapped to a coordinates [Longitude, Latitude]. Apple has introduced a new location verification API just for App Clips that allows you to do a one-time check to see if the App Clip code, NFC tag or QR code that the user scanned is where it says it is.
Enable Your App Clip to Verify the User’s Location
To enable your App Clip to verify the user’s location, modify your App Clip’s Info.plist file:
Open your App Clip’s Info.plist, add the NSAppClip key, and set its
type to Dictionary.
Add an entry to the dictionary with NSAppClipRequestLocationConfirmation as the key, select Boolean as
its type, and set its value to true.
But using App Clip Location services is different:
Parse the information on the URL that launches the App CLip
Send a request to your Database to fetch the location information for this business
Use activity.appClipActivationPayload to confirm if the location (in Step 2) is in region where the user is right now.
The Code bellow (Copied from Apple) explains how to do it.
import UIKit
import AppClip
import CoreLocation
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// Call the verifyUserLocation(_:) function in all applicable life-cycle callbacks.
func verifyUserLocation(_ activity: NSUserActivity?) {
// Guard against faulty data.
guard activity != nil else { return }
guard activity!.activityType == NSUserActivityTypeBrowsingWeb else { return }
guard let payload = activity!.appClipActivationPayload else { return }
guard let incomingURL = activity?.webpageURL else { return }
// Create a CLRegion object.
guard let region = location(from: incomingURL) else {
// Respond to parsing errors here.
return
}
// Verify that the invocation happened at the expected location.
payload.confirmAcquired(in: region) { (inRegion, error) in
guard let confirmationError = error as? APActivationPayloadError else {
if inRegion {
// The location of the NFC tag matches the user's location.
} else {
// The location of the NFC tag doesn't match the records;
// for example, if someone moved the NFC tag.
}
return
}
if confirmationError.code == .doesNotMatch {
// The scanned URL wasn't registered for the App Clip.
} else {
// The user denied location access, or the source of the
// App Clip’s invocation wasn’t an NFC tag or visual code.
}
}
}
func location(from url:URL) -> CLRegion? {
// You should retrieve the coordinates from your Database
let coordinates = CLLocationCoordinate2D(latitude: 37.334722,
longitude: 122.008889)
return CLCircularRegion(center: coordinates,
radius: 100,
identifier: "Apple Park")
}
}
And that’s it, this his how your support multiple businesses with App Clip

switching app language without restarting the app swift

I want my app language to change on button click..
My code is:
#IBAction func convertlang(_ sender: Any) {
if L102Language.currentAppleLanguage() == "en" {
L102Language.setAppleLAnguageTo(lang: "ar")
UIView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
L102Language.setAppleLAnguageTo(lang: "en")
UIView.appearance().semanticContentAttribute = .forceLeftToRight
}
}
let APPLE_LANGUAGE_KEY = "AppleLanguages"
class L102Language {
class func currentAppleLanguage() -> String{
let userdef = UserDefaults.standard
let langArray = userdef.object(forKey: APPLE_LANGUAGE_KEY) as! NSArray
let current = langArray.firstObject as! String
return current
}
class func setAppleLAnguageTo(lang: String) {
let userdef = UserDefaults.standard
userdef.set([lang,currentAppleLanguage()], forKey: APPLE_LANGUAGE_KEY)
userdef.synchronize()
}}
and this works fine and convert the language when i click on the button..
the problem is i need to restart the app to see the language change..
i searched for this but most of them were for objective-c and tried some for swift but didn't work..
how to do this?
You can't change the language at runtime with normal button click. If you really need that, you need to use a custom localization system instead.
To change the app language during runtime, I have done this manually by creating two different storyboards. I saved the language preference in NSUserDefaults but NOT with the key AppleLanguages and then called AppDelegate's didFinishLaunchingWithOptions method to select storyboard.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainEnglish" bundle:nil];
UINavigationController *navigationcontroller=[[UINavigationController alloc]init];
RegistorViewController *registor=[storyboard instantiateViewControllerWithIdentifier:#"Registor"];
[self.window setRootViewController:navigationcontroller];
[self.window makeKeyAndVisible];
[navigationvontroller pushViewController:registor animated:NO];
Though this is not what apple wants a developer to do in runtime.
Someone even asked an Apple engineer and this is the response:
In general, you should not change the iOS system language (via use of the AppleLanguages pref key) from within your application. This goes against the basic iOS user model for switching languages in the Settings app, and also uses a preference key that is not documented, meaning that at some point in the future, the key name could change, which would break your application.
If you want to switch languages in your application, you can do so via manually loading resource files in your bundle. You can use NSBundle:pathForResource:ofType:inDirectory:forLocalization: for this purpose, but keep in mind that your application would be responsible for all loading of localized data.
I had faced the same problem in my project recently. You are using the correct code to change app language but it change only string file data not the storyboard. For complete localisation you have to restart the app again, because there is no any another way to do this. You can show a popup to user "Please restart your app to change language completely". Because we can kill the app programatically, but after doing this there is no way to restart the app again.
This might not be the most iOS-typical way of doing things, but I tend to treat static content like any other data in the app, especially when I'm using VIPER (although other patterns would work) and a unidirectional data flow.
With that in mind, for this I would have a service accessing a store of static content keyed of the user's language preference. When that changes I would trigger a refresh of the content.
This has worked well for projects when I also have server data as well as local data that needs to be localised.
To be fair, I've never used the standard tools much. And I suspect this may not be ideal for right-to-left language types.

How to launch system apps in an iOS Xcode UI test case

I've got an app whose main purpose is to enter data into HealthKit. I'd like to write some Xcode UI tests to verify that it's writing this data successfully, but I'm having some difficulty verifying the data in the Health app.
When I initially recorded my test, it skipped my simulated Home button press, but it was recording as I swiped over to the first home screen and navigated into the Health app to show the data points.
I searched for how to press the Home button, and found this (which works):
XCUIDevice.shared.press(.home)
However, none of the other calls it recorded actually work for navigation outside of the app. The recorded code for swiping on the home screen obviously looks wrong, and also doesn't work when I replace tap() with a swipeRight() or swipeLeft():
app.childrenMatchingType(.Window).elementBoundByIndex(1).childrenMatchingType(.Other).elementBoundByIndex(1).childrenMatchingType(.Other).element.childrenMatchingType(.Other).element.childrenMatchingType(.Other).elementBoundByIndex(0).childrenMatchingType(.ScrollView).element.tap()
The next couple of lines, for launching an app on the home screen, don't even work for an app icon that's on the currently visible page:
let elementsQuery = app.scrollViews.otherElements
elementsQuery.icons["Health"].tap()
Is there any way to achieve what I'm trying to do, or will I need to wait to verify end-to-end testing until I add the ability to read from HealthKit to my app?
Xcode 9
Here's the solution using Xcode 9
let messageApp = XCUIApplication(bundleIdentifier: "com.apple.MobileSMS")
messageApp.activate()
You can find a list of bundle identifier for the system apps in this post
Xcode 8
For Xcode 8 it's a little bit more complicated
In order to launch an application from the Springboard you need to import the following headers
https://github.com/facebook/WebDriverAgent/blob/master/PrivateHeaders/XCTest/XCUIElement.h
https://github.com/facebook/WebDriverAgent/blob/master/PrivateHeaders/XCTest/XCUIApplication.h
Then use the following (for example with Health)
Objective-C
#interface Springboard : NSObject
+ (void)launchHealth;
#end
#implementation Springboard
+ (void)launchHealth
{
XCUIApplication *springboard = [[XCUIApplication alloc] initPrivateWithPath:nil bundleID:#"com.apple.springboard"];
[springboard resolve];
XCUIElement *icon = springboard.icons[#"Health"];
if (icon.exists) {
[icon tap];
// To query elements in the Health app
XCUIApplication *health = [[XCUIApplication alloc] initPrivateWithPath:nil bundleID:#"com.apple.Health"];
}
}
#end
Swift
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
class func launchHealth() {
springboard.resolve()
let icon = springboard.icons["Health"]
if icon.exists {
icon.tap()
// To query elements in the Health app
let health = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.Health")
}
}
}
Swift 4
let app = XCUIApplication(bundleIdentifier: "com.apple.springboard")
you bounded your UI tests with your application and the moment you press home button and move out of the application UI you can not perform UI operations as app variable in your code is pointing to your application.
you may be having code like
let app = XCUIApplication()
So you should modify that XCUIApplication() line.
let app = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
now you can move out of application.
According to my knowledge it is good to have and independent UITestAgent app and initiate its UiTestcases with springboard bundle id so you can test any application with the help of that app not like i coded some test case inside product XYZ code base and for next product ABC I will write tests inside ABC product's code base!

Resources