XCode UITesting check if a text field exists - objective-c

I can't find any way to check if a text field exists without trying to get it which then fails the tests and shows an error if it can't be found. No matches found for TextField
Current code
XCUIElement *usernameTextField = app.textFields[#"username"];
Reason/detail
I've got a Objective C UITest in XCode which logs into my app in setUp and logs out in tearDown however sometimes my app is already logged in when the test starts (if the simulator has been used for anything else in the meantime). I'd like to be able to check to see if the username textfield exists in my setUp and then if it doesn't I can skip the login or call my logout function and continue as normal.

Not sure about Obj-C but here's how it would work in Swift.
let usernameTextField = app.textFields["username"]
if usernameTextField.exists {
do something
} else {
do something else
}

Here is the code in Swift, which can easily be converted to Obj-C:
// given:
// usernameTextField exists
// The username that is possibly entered there is "username".
// then:
if usernameTextField.value as! String == "username" {
// logged in
} else {
// not logged in
}

Related

Apple Media Library Access Permission retrieved programmatically

I would appreciated some help please even if this is maybe a trivial question.
I've written a SwiftUI app that reads the media library from the device and plays it depending on user settings. That is all fine.
The problem I have is that if you install the app for the first time, the user needs to grant permission to access the media library. This appears to be a system generated dialog but I cannot see which step in the also triggers it. I tried to have the access request be triggered code generated but that doesn't seem to trigger the pop up but it still only appears at a later stage in the app load process. The code seems to recognise though that the user reacted to the access request pop up and does select the correct switch case.
What it does not seem to do though is that it still can't read the media library. The MPMediaQuery returns nil.
My suspicion is that it somehow connected to the fact that the access request doesn't run on the main thread but I am not experienced enough in Swift programming to know what the problem is. I would be most grateful for some helpful hints.
Here is my code:
import MediaPlayer
import SwiftUI
import Foundation
class Library {
var artists : [Artist] = []
#EnvironmentObject var settings : UserSettings
var counter : Float = 0
init() {
switch MPMediaLibrary.authorizationStatus() {
case .authorized:
print("authorized")
case .denied:
print("denied")
return
case .notDetermined:
print("not determined")
MPMediaLibrary.requestAuthorization() { granted in
if granted != .authorized {
return
}
}
case .restricted:
print("restricted")
#unknown default:
print("default")
}
if MPMediaLibrary.authorizationStatus() == .notDetermined { return }
let filter : Set<MPMediaPropertyPredicate> = [MPMediaPropertyPredicate(value: MPMediaType.music.rawValue, forProperty: MPMediaItemPropertyMediaType)]
let mediaQuery = MPMediaQuery(filterPredicates: filter )
var artistsInCollection : [Artist] = []
guard let _ = mediaQuery.items?.count else { return }
for item in mediaQuery.items! {
//here I do something but that's not relevant to my question
}
self.artists = artistsInCollection
}
}

Check if user has changed the device passcode in Swift or Objective C

I'm wondering if there's a way to check if a user has changed the device passcode since the last time he oppenend my app.
I know it's possible to get this information with fingerprints (added or removed fingerprints) with something like this:
let context = LAContext()
context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: nil)
if let domainState = context.evaluatedPolicyDomainState
where domainState == oldDomainState {
// Enrollment state the same
} else {
// Enrollment state changed
}
but I would like to know if it's possible just with the device passcode.
Thanks in advance!

Checking authorizations for MPMediaLibrary in Swift 3

I am using the following code to check the MPMediaLibrary authorizations:
func handlePermissions() {
let permissionStatus = MPMediaLibrary.authorizationStatus()
switch (permissionStatus) {
case MPMediaLibraryAuthorizationStatus.authorized:
print("permission status is authorized")
case MPMediaLibraryAuthorizationStatus.notDetermined:
print("permission status is not determined")
MPMediaLibrary.requestAuthorization(MPMediaLibraryAuthorizationStatus -> permissionStatus)
case MPMediaLibraryAuthorizationStatus.denied:
print("permission status is denied")
case MPMediaLibraryAuthorizationStatus.restricted:
print("permission status is restricted")
}
}
Ultimately, I am trying to prompt the user for their authorization (upon launch) prior to calling a query...via the case MPMediaLibraryAuthorizationStatus.notDetermined:. The code above produces the error: Expected type after '->'. When the requestAuthorization() line is commented out, the app crashes upon launch (access has not been authorized) and the authorization prompt view is shown after the launch screen disappears.
I've seen some examples of how to perform requestAuthorization() in Objective C but nothing in Swift. I don't understand:
MPMediaLibrary.requestAuthorization( handler: (MPMediaLibraryAuthorizationStatus) -> Void )
What is the proper way to request authorization for access to the MPMediaLibrary in Swift 3?
You've actually used the prototype of the requestAuthorization method. You need to adapt it to your own use.
MPMediaLibrary.requestAuthorization( handler: (MPMediaLibraryAuthorizationStatus) -> Void )
means that requestAuthorization take a function as parameter and this function takes a MPMediaLibraryAuthorizationStatus as parameter an return nothing.
For example if I want to request the authorisation and then display the result inside my console. I first check if the application is not already authorised :
if authoriationStatus != .authorized {
MPMediaLibrary.requestAuthorization({
(status) in
switch status {
case .notDetermined:
print("notDetermined")
case .denied:
print("denied")
case .restricted:
print("restricted")
case .authorized:
print("authorized")
}
})
}
As you can see, I used a function as a parameter for the method requestAuthorization. The function is described inside {...}. It's called a closure and it's something you always use in Swift.
For swift 4.2 to check authorisations for MPMediaLibrary
import MediaPlayer
let status = MPMediaLibrary.authorizationStatus()
switch status {
case .authorized:
self.openMusicLibrary()
break
case .notDetermined:
MPMediaLibrary.requestAuthorization() { status in
if status == .authorized {
DispatchQueue.main.async {
self.openMusicLibrary()
}
}
}
break
case .denied:
//show alert
print("Please Allow Access to the Media & Apple Music from appliction setting.")
break
case .restricted:
break
}

Location authorization error even though I've asked for authorization in iOS 8 using swift

I have an app using the location services. If the app first starts, it ask the user for permission.
For some reason, if I tap on "Allow" I'll get this message:
Trying to start MapKit location updates without prompting for location authorization.
I know what this means, but I've set breakpoints all over my code and I am SURE that nothing tries to read the user location before it is allowed to do so.
Anyway, I seem to be missing something.
1) Is there a "common mistake" which one could do, something within the storyboard or so?
2) Will Apple reject an app that has such an error?
Thing is that the app works perfectly well, The only thing is that I see this message within the console. I don't know whether Apple will see this message too and if this would be a reason to reject an app..
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) { // iOS8+
// Sending a message to avoid compile time error
[[UIApplication sharedApplication] sendAction:#selector(requestWhenInUseAuthorization)
to:self.locationManager
from:self
forEvent:nil];
} else {
[self.locationManager startUpdatingLocation];
}
}
I think you may need to include something like this for the requestWhenInUseAuthorization
Assume that you are using CLLocationManager. So did you make a strong reference to your locationManager object?
It seems to be a case when you requested location in local scope (variable) inside a function. Then trying to use MapKit, but locationManager object is already deallocated.
To solve that case, you should declare...
var locationManager = CLLocationManager()
... as an instance variable, then request authorization, and then using location services.
The problem was I have asked for authorization to use location services if the app is in use and I have determined the authorization like this:
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = GpsStatus.restricted
break
case CLAuthorizationStatus.Denied:
locationStatus = GpsStatus.denied
break
case CLAuthorizationStatus.NotDetermined:
locationStatus = GpsStatus.notDeterminded
break
default:
locationStatus = GpsStatus.allowed
break
}
}
This seems to be wrong, the error is gone if I explicitly check for CLAuthorizationStatus.AuthorizedWhenInUse:
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = GpsStatus.restricted
break
case CLAuthorizationStatus.Denied:
locationStatus = GpsStatus.denied
break
case CLAuthorizationStatus.NotDetermined:
locationStatus = GpsStatus.notDeterminded
break
case CLAuthorizationStatus.AuthorizedWhenInUse:
locationStatus = GpsStatus.allowed
default:
locationStatus = GpsStatus.notDeterminded
break
}
}
EDIT:
Seems like it's also a problem to add a TrackingLocationButton before I have the permission to use location services.
So do this
var userTrackingButton = MKUserTrackingBarButtonItem(mapView: self.mapView);
self.toolBar.items?.insert(userTrackingButton, atIndex: 0)
only if you have the permission
For iOS 8 you need to define the "Privacy - Location Usage Description" in the Info.plist.
Eg. NSLocationWhenInUseUsageDescription = "Use your location to show near by stores".
or
NSLocationAlwaysUsageDescription = "Use your location to show near by stores".
This key specifies the reason for accessing the user’s location information.

Firebase OSX simple login with email/pass oddity

I'm setting up a very basic OSX app for an existing firebase with SimpleLogin and email/password authentication.
Here's the code I'm using.
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
Firebase* ref = [[Firebase alloc] initWithUrl:#"https://myapp.firebaseio.com"];
FirebaseSimpleLogin* authClient = [[FirebaseSimpleLogin alloc] initWithRef:ref];
[authClient loginWithEmail:#"myemail#mydomain.com" andPassword:#"mypassword"
withCompletionBlock:^(NSError* error, FAUser* user) {
if (error != nil) {
// There was an error logging in to this account
NSLog(#"authClient login error: %#", error);
} else {
NSLog(#"Login success.");
}
}];
}
Login is successful, and I see the log output. However, "FAUser* user" is nil. How? Why?
Online search / existing SO questions haven't helped..
Any ideas?
** UPDATE **
Same code in iOS works as expected. Is this just an OSX issue?
** UPDATE 2 **
I compiled the source code from the Firebase/Objective-C Simple Login Service (which seems to only reference iOS) directly in my OSX project and found that there is a "duplicate item" error code when the login service tries to store Keychain data on OSX.
The source code after the keychain save operation then proceeds to return a null user. I believe there is a logic error here because the if statement evaluates to true whenever it is not a success code (skipping the special case for duplicate item):
if (status != noErr) {
user = nil;
}
else if (status == errSecDuplicateItem) {
// TODO: log an error?
user = nil;
}
Anyway, I am able to continue working by modifying this small chunk of code to fit my needs.
Yeah, you're right. It's an error with accessing Keychain.
To clear the error on your own machine, you can go to Keychain Access, search for 'firebase' in the upper right. You should get an item with the name https://auth.firebase.com. Click it. Check that it says Firebase_<YOUR_FIREBASE_NAME> for the Account field, and go ahead and delete it. The next time you spin up your app, you'll have to log in again.
This seems to show up when you try to hit the same keychain item many times at once. Accessing the keychain slowly multiple times seems to be fine, so unless someone is authenticating many of the same app on the same machine, you shouldn't get the error. (If you can hit it some other way, please share!)