iOS/Swift test for UIAlertController/UIAlertView - ios

I'm developing an app that needs to run on iOS 7 as well as 8. I want to use the UIAlertController if possible, if not fall back to UIAlertView.
I use this test:
let gotUIAlertController:AnyClass? = NSClassFromString("UIAlertController")
if( gotUIAlertController != nil )
{
// Do UIAlertController
}
else
{
// DO UIAlertView
}
This appears to work on iOS8 simulator, in debug mode but when in release mode (or running debug mode with instruments), gotUIAlertController is not nil, so the UIAlertController is trying to present and the app crashes. The deployment target is 7.1 with the bas SDK set to 8.1
Can anyone enlighten me on why this code is executing in such a way on iOS7.1?

I don't know why, but the workaround I found is:
let gotUIAlertController: AnyObject? = objc_getClass("UIAlertController".UTF8String)
if( gotUIAlertController != nil ) {
// Do UIAlertController
}
else {
// DO UIAlertView
}

Related

Detecting if iPhone or iPad without opting for Universal App

I really want this to work:
if UIDevice.current.userInterfaceIdiom == .pad {
print("iPad")
} else {
print("not iPad")
}
However, my app only prints "not iPad" even though I am using an iPad. I have Devices (under Deployment Info) set to iPhone. If I change this to Universal it works, but I don't want a universal app, I just want to be able to detect if an iPhone or iPad is being used (even though the app is for iPhones, due to compatibility mode it still can be run on iPads).
So how can I detect if the device is an iPad or iPhone without changing my app to Universal? Thanks
You can check the model:
if UIDevice.current.model.hasPrefix("iPad") {
print("it is an iPad")
} else {
print("it is not an iPad")
}
One thing you can do is get the inner-screen width of the page. Phones are generally below 786 px and you can call everything else an iPad. Use can do something like this
var width = window.innerWidth;
If (width > 786px) {
print(‘ipad’);
} else {
print(‘not ipad’)
}
iPhone Only app can be downloaded to iPad. But in current scenario, we doesn't have device that's much smaller resolution(deployment target: 9).
OBJ-C
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && [[[UIDevice currentDevice] model] hasPrefix:#"iPad"]) {
// This app is an iPhone app running on an iPad
NSLog(#"This app is an iPhone app running on an iPad");
}
Swift
if UIDevice.current.userInterfaceIdiom == .phone, UIDevice.current.model.hasPrefix("iPad") {
print("iPad")
}
Try this,
print("Model - \(UIDevice.current.model)")

RPScreenRecorder stopRecording block not getting called

I have searched enough but failed to get a solution.
I am using ReplayKit to record the screen of my app. I have started recording the screen by calling
let sharedRecorder = RPScreenRecorder.shared()
sharedRecorder.startRecording() { error in
if let error = error {
self.showScreenRecordingAlert(message: error.localizedDescription)
}
}
When I am pressing the stopRecord button I am calling
let sharedRecorder = RPScreenRecorder.shared()
sharedRecorder.stopRecording { previewViewController, error in
if let error = error {
self.showScreenRecordingAlert(message : error.localizedDescription)
return
}
}
But the issue that I am facing is, the program control does not enter inside the stopRecording block.
When I am doing po sharedRecorder.isRecording, it always returns false.
I have done everything I know but failed to get a solution.
If you having this above issue with your code i have found the solution for this.
let sharedRecorder = RPScreenRecorder.shared()
sharedRecorder.stopRecording { previewViewController, error in
if let error = error {
self.showScreenRecordingAlert(message : error.localizedDescription)
return
}}
Above Block will not call if you running your app on simulator so please use real device to test then above method will call definitely.
Thank you.
Just had this issue running XCode 9.4.1 and building onto iOS 11.4.0. Upgrading the phone to iOS 11.4.1 fixed the bug. I'm not sure if the difference in XCode versions is the root cause or if 11.4.0 was just broken.

iOS 11 Simulator not allowing LAContext and FaceID

I've running the latest Xcode 9 GM (13 Sep 2017) and have set Hardware > Face ID > Enrolled in simulator as well as Deployment Target 11.0. However I'm getting error code -6 LAErrorTouchIDNotAvailable.
Is there some setting I'm missing?
let myContext = LAContext()
let myLocalizedReasonString = "You are pretty"
var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
if success {
print("// User authenticated successfully, take appropriate action")
} else {
print(" // User did not authenticate successfully, look at error and take appropriate action")
}
}
} else {
print(" // Could not evaluate policy; look at authError and present an appropriate message to user")
}
} else {
print(" // Fallback on earlier versions")
}
Face ID does not work in the Xcode 9 GM due to a framework bug. Xcode 9.1 fixes this issue.
You can test your app in the iPhone 8 simulator and ensure it works correctly with Touch ID or run the Xcode 9.1 beta and test Face ID support there.
I think the iphone X simulator's faceID doesn't work at the moment, hopefully they will fix it soon...
https://forums.developer.apple.com/thread/86779
we could do a bug report to see if it speed things along :P
https://developer.apple.com/bug-reporting
Face ID is working now, with Xcode 9.1. Follow these steps to test it in Simulator.
Add privacy statement in your target's info.plist file.
Import LocalAuthentication framework to your project and add following code to your view controller and try with Face-ID
import LocalAuthentication
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
localAuthentication()
}
func localAuthentication() -> Void {
let laContext = LAContext()
var error: NSError?
let biometricsPolicy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
if (laContext.canEvaluatePolicy(biometricsPolicy, error: &error)) {
if let laError = error {
print("laError - \(laError)")
return
}
var localizedReason = "Unlock device"
if #available(iOS 11.0, *) {
if (laContext.biometryType == LABiometryType.faceID) {
localizedReason = "Unlock using Face ID"
print("FaceId support")
} else if (laContext.biometryType == LABiometryType.touchID) {
localizedReason = "Unlock using Touch ID"
print("TouchId support")
} else {
print("No Biometric support")
}
} else {
// Fallback on earlier versions
}
laContext.evaluatePolicy(biometricsPolicy, localizedReason: localizedReason, reply: { (isSuccess, error) in
DispatchQueue.main.async(execute: {
if let laError = error {
print("laError - \(laError)")
} else {
if isSuccess {
print("sucess")
} else {
print("failure")
}
}
})
})
}
}
}
FaceID authentication will prompt you for first time to allow FaceID detection for your app.
Now enable Face ID enrolment and run your app to test Face ID simulation Testing.
Here is simulation result for matching and non-matching faces.
Result for matching face:
Result for non-matching face:
XCode 9.1 beta came out today in which the original code should work perfectly in the simulator!
According to Apples Documentation for LAContext, we need to add the key NSFaceIDUsageDescription with a reason of use String, as that will display the authorisation request for the use of FaceId on the device.
Example add this to info.plist:
NSFaceIDUsageDescription
set it to type String, and add a text that you want to be shown, in the prompt request for access to the Face ID camera.
"Your app" request your permission to use Face ID, for you to login to your account / unlock your notes / what ever reason in the end.
By adding this, you can go to the simulator for iPhone X, and you will be prompted for the Face ID, press accept, and everything should work perfectly.
Remember to enrol biometry support for the simulator by going into
Simulator -> Hardware -> Face ID / Touch ID -> Enrolled
Then you just need to pressed the Match / Non-Matching Touch / Face ID, to test out your handling
For more details and check out Apple's documentation: https://developer.apple.com/documentation/localauthentication/lacontext
---- Edit ----
This worked for me in both Xcode 9.0 and 9.1

Can use DJI SDK to replicate "Pitch Lock" feature of DJI Go app?

I'm trying to replicate the "Pitch Lock" on / off feature of the DJI Go app. How can I do this?
I'm using XCode 8.2.1, building for iOS 10.1, connecting to an Osmo Mobile with an iPhone 6s attached. The Osmo Mobile has the latest firmware (version 01.30.01.52).
Everything works so far (registerApp, connecting via bluetooth, getting handheld button presses, getting gimbal battery updates, getting gimbal updates).
Setting setGimbalWorkMode to either .freeMode or .yawFollowMode doesn't seem to have any effect. No error is returned in the completion block, but there's no effect on Gimbal operation.
The gimbal behaves as if it is in .freeMode (always moves to the exact direction handheld stick is pointing), but DJIGimbalDelegate only receives .yawFollowMode updates (which is what the pitchLock mode should do).
Setting setGimbalWorkMode to other modes results in an error (as expected with Osmo Mobile device).
Here's how I'm trying to toggle pitchLock on/off.
#IBAction func pitchLockPressed(_ sender: UIButton) {
pitchLock = !pitchLock
if let gimbal = fetchGimbal() {
var workMode : DJIGimbalWorkMode = .freeMode // .freeMode .fpvMode and .unknown return error using Osmo Mobile
if pitchLock {
workMode = .yawFollowMode
}
gimbal.setGimbalWorkMode(workMode, withCompletion: { (error) in
if (error != nil) {
print("error workMode: \(error?.localizedDescription)")
self.pitchLock = !(self.pitchLock) // back to previous
}
})
}
}
Here's the delegate, which only reports .yawFollowMode no matter what I do:
func gimbal(_ gimbal: DJIGimbal, didUpdate gimbalState: DJIGimbalState) {
// var needUpdate = false
if lastReportedWorkMode != gimbalState.workMode {
lastReportedWorkMode = gimbalState.workMode
switch lastReportedWorkMode {
case DJIGimbalWorkMode.fpvMode:
print("FPV\n")
case DJIGimbalWorkMode.freeMode:
print("Free\n")
case DJIGimbalWorkMode.yawFollowMode:
print("Yaw-follow\n")
case DJIGimbalWorkMode.unknown:
print("Unknown\n")
}
}
Anyone getting setGimbalWorkMode to actually change gimbal modes?

EXC_BAD_ACCESS (code=1) but only on device since upgrading to Xcode 5.1b4 with IOS7.1b4

I have a segmented control I wrote myself (UISegmenterControl) which sends a message once the selected segment has changed:
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex
{
if(_selectedSegmentIndex != selectedSegmentIndex) {
NSInteger segmentIndex = 0;
for (UISegmenterControlSegment *segmentButton in self.segmentButtons) {
if (segmentIndex == selectedSegmentIndex) {
[segmentButton setSelected:YES];
_selectedSegmentIndex = selectedSegmentIndex;
if (self.target != nil) {
if ([self.target respondsToSelector:self.action]) {
objc_msgSend(self.target, self.action, self);
}
}
}
else {
[segmentButton setSelected:NO];
}
segmentIndex++;
}
}
}
The objc_msgSend fires off (void)didChangeSegmentControl:(UISegmenterControl *)control in my view controller whilst passing a pointer to the segmented control itself.
Problem is, since upgrading to XCode 5.1 beta 4 and my development device to IOS 7.1 beta 4 my code is falling over on the device with EXC_BAD_ACCESS (code=1) when didChangeSegmentControl fires off.
It seems that the pointer to the segmented control (i.e. the 'control' parameter) is being lost between the objc_msgSend and didChangeSegmentControl.
What's confusing me most here is that this wasn't happening until I upgraded and still doesn't happen either on the simulator or on my device when profiling the app. I guess what I'm asking is: does this look to be a problem with my code (that somehow functioned perfectly fine on previous IOSes and/ or XCodes) or might this be an issue with the latest beta?
Please let me know if you need any more information about my app.
Rather than using objc_msgSend, can you use the following instead:
[self.target performSelectorOnMainThread:self.action withObject:self waitUntilDone:YES];
P.S. Thanks for the update on the correct objects in the comments

Resources