Checking authorizations for MPMediaLibrary in Swift 3 - authorization

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
}

Related

Why is the value not entering the list?

At 'urichecking2' log, I can see there is value. But in 'uriChecking' the uriList is null.
why the uriList.add not work??
private fun getPhotoList() {
val fileName = intent.getStringExtra("fileName")
Log.d("fileNameChecking", "$fileName")
val listRef = FirebaseStorage.getInstance().reference.child("image").child(fileName!!)
var tmpUrl:Uri = Uri.parse(fileName)
Log.d("firstTmpUri","$tmpUrl")
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
tmpUrl = task.result
Log.d("secondTmpUri","$tmpUrl")
Log.d("urichecking2","$task.result")
uriList.add(task.result)
} else {
}
}.addOnFailureListener {
// Uh-oh, an error occurred!
}
}
}
Log.d("thirdTmpUri","$tmpUrl")
Log.d("urichecking", "$uriList")
}
If I do this, the log is output in the order of first, third, and second, and the desired value is in second, but when third comes out, it returns to the value of first.
The listAll method (like most cloud APIs these days, including downloadUrl which you also use) is asynchronous, since it needs to make a call to the server - which may take time. This means the code executes in a different order than you may expect, which is easiest to see if you add some logging:
Log.d("Firebase","Before starting listAll")
listRef.listAll()
.addOnSuccessListener { listResult ->
Log.d("Firebase","Got listResult")
}
Log.d("Firebase","After starting listAll")
When you run this code it outputs:
Before starting listAll
After starting listAll
Got listResult
This is probably not the order you expected, but it perfectly explains why you can't see the list result. By the time your Log.d("urichecking", "$uriList") runs, none of the uriList.add(task.result) has been called yet.
The solution for this is always the same: any code that needs the list result, has to be inside the addOnCompleteListener callback, be called from there, or be otherwise synchronized.
So in its simplest way:
listRef.listAll()
.addOnSuccessListener { listResult ->
for (item in listResult.items) {
item.downloadUrl.addOnCompleteListener { task ->
if (task.isSuccessful) {
uriList.add(task.result)
Log.d("urichecking", "$uriList")
}
}
}
}
This is an incredibly common mistake to make if you're new to programming with asynchronous APIs, so I recommend checking out
Asynchronous programming techniques in the Kotlin language guide
How to get URL from Firebase Storage getDownloadURL
Can someone help me with logic of the firebase on success listener
Why does my function that calls an API or launches a coroutine return an empty or null value?

Kotlin ConflatedBroadcastChannel.offer() doesn't work?

I am sending a value via MyRepository.myConflatedChannel.offer(myvalue).
I then expect to receive it in collect { } or onEach { } blocks in my ViewModel. However, neither function is invoked. It is as if nothing is passed down the ConflatedBroadcastChannel.
Has anybody seen a similar problem?
Make sure you properly work with receiving values.
If you use the ConflatedBroadcastChannel, you can use either OpenSubscription to get a ReceiveChannel or you can represent it as flow (with asFlow).
Note that consume and consumeEach are terminal, they perform an action and then cancel the channel after the execution of the block. See this.
First case:
val receivingChannel = MyRepository.myConflatedChannel.openSubscription()
// then you can consume values using for example a for loop, e.g.:
launch {
for (value in receivingChannel) {
// do something
}
}
Second case:
val receivingFlow = MyRepository.myConflatedChannel.asFlow()
launch {
receivingFlow.collect {
// do something
}
}

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

Detect a user logout on macOS

I am currently tryting to detect a user logout in macOS 10.14 (Mojave). I found this, which was working in the past:
Catching Logoff (not power off) event on MAC using objective C
The code I use is:
NSAppleEventManager* m = [NSAppleEventManager sharedAppleEventManager];
NSAppleEventDescriptor* desc = [m currentAppleEvent];
switch ([[desc attributeDescriptorForKeyword:kAEQuitReason] int32Value])
{
case kAELogOut:
case kAEReallyLogOut:
// log out
break;
case kAEShowRestartDialog:
case kAERestart:
// system restart
break;
case kAEShowShutdownDialog:
case kAEShutDown:
// system shutdown
break;
default:
// ordinary quit
break;
}
But the value I get is always zero (0).
Did something change in Mojave or is there another mechanism? This code is called in the applicationShouldTerminate function in my AppDelegate.
It's still available and it works (just tested on macOS Big Sur), but the value is in the enumCodeValue property.
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let reason = NSAppleEventManager.shared()
.currentAppleEvent?
.attributeDescriptor(forKeyword: kAEQuitReason)
switch reason?.enumCodeValue {
case kAELogOut, kAEReallyLogOut:
print("Logout")
return .terminateNow
case kAERestart, kAEShowRestartDialog:
print("Restart")
return .terminateNow
case kAEShutDown, kAEShowShutdownDialog:
print("Shutdown")
return .terminateNow
case 0:
// `enumCodeValue` docs:
//
// The contents of the descriptor, as an enumeration type,
// or 0 if an error occurs.
print("We don't know")
return .terminateNow
default:
print("Cmd-Q, Quit menu item, ...")
return .terminateNow
}
}

XCode UITesting check if a text field exists

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
}