I want my YouTubePlayer State to change a public variable in my view controller. I'm having trouble with the YTPlayer Delegate function and the switch. Here is my var...
var ytps = String!
here is the youtube state function...
func playerView(player: YTPlayerView!, didChangeToState state: YTPlayerState)
{
switch (state)
{
case YTPlayerState.Ended:
// handle ended state
print("Ended")
break;
case YTPlayerState.Paused:
// handle paused state
print("Paused")
break;
default:
break;
}
}
How can I get the state of either YTPlayerState.Ended or YTPlayerState.Paused to change the value of my empty string to "Ended/Paused"?
I guess you could just replace print("Paused") and print("Ended") with ytps = "Paused" and ytps = "Ended". Full example:
var ytps = String()
func playerView(player: YTPlayerView!, didChangeToState state: YTPlayerState) {
switch state {
case YTPlayerState.Ended:
ytps = "Ended"
break
case YTPlayerState.Paused:
ytps = "Paused"
break
default:
break
}
}
Related
Does anyone have any references on unit testing key value observing logic in swift? I'd like to mock the AVPlayerItem's status changes in my unit tests to confirm that the expected logic is triggered. For simplicity this is what I have so far:
A function that observes the provided playerItem's status
public func observePlayerItemStatus(playerItemToObserve: AVPlayerItem) -> NSKeyValueObservation {
let observerToken = playerItemToObserve.observe(\.status, options: [.new, .old], changeHandler: { (playerItemToObserve, _) in
switch playerItemToObserve.status {
case .failed:
// failed logic
case .readyToPlay:
// ready to play logic
default:
// default logic
}
})
return observerToken
}
I've created a mock class, MockAVPlayerItem
class MockAVPlayerItem: AVPlayerItem {
var mockStatus: AVPlayerItem.Status = .unknown
public override var status: AVPlayerItem.Status {
get { return mockStatus }
set { self.mockStatus = newValue }
}
}
My unit test attempting to trigger the status code change does the following:
func test_readyToPlayIsTriggered() {
let url = URL(string: "https://www.rmp-streaming.com/media/big-buck-bunny-360p.mp4")!
let mockPlayerItem = MockPlayerItem(url: url)
let statusToken = observePlayerItemStatus(playerItemToObserve: mockPlayerItem)
mockPlayerItem.status = .readyToPlay
... some assertion that ready to play was called ....
}
The logic itself works when I run the application however my unit tests are unable to trigger my observer. I've followed a similar approach to mock AVPlayer.timeControlStatus and it's worked great but for some reason AVPlayerItem.status isn't behaving the same.
Using a willChangeValue and didChangeValue in my mock solved my problem. Observers are now triggered as expected.
class MockAVPlayerItem: AVPlayerItem {
var mockStatus: AVPlayerItem.Status = .unknown
public override var status: AVPlayerItem.Status {
get { return mockStatus }
set {
willChangeValue(for: \.status)
self.mockStatus = newValue
didChangeValue(for: \.status)
}
}
}
I've done a ton of searching and read a bunch of articles but I cannot get SwiftUI to dynamically update the view based on changing variables in the model, at least the kind of thing I'm doing. Basically I want to update the view based on the app's UNNotificationSettings.UNAuthorizationStatus. I have the app check the status on launch and display the status. If the status is not determined, then tapping on the text will trigger the request notifications dialog. However, the view doesn't update after the user either permits or denies the notifications. I'm sure I'm missing something fundamental because I've tried it a dozen ways, including with #Published ObservableObject, #ObservedObject, #EnvironmentObject, etc.
struct ContentView: View {
#EnvironmentObject var theViewModel : TestViewModel
var body: some View {
VStack {
Text(verbatim: "Notifications are: \(theViewModel.notificationSettings.authorizationStatus)")
.padding()
}
.onTapGesture {
if theViewModel.notificationSettings.authorizationStatus == .notDetermined {
theViewModel.requestNotificationPermissions()
}
}
}
}
class TestViewModel : ObservableObject {
#Published var notificationSettings : UNNotificationSettings
init() {
notificationSettings = type(of:self).getNotificationSettings()!
}
func requestNotificationPermissions() {
let permissionsToRequest : UNAuthorizationOptions = [.alert, .sound, .carPlay, .announcement, .badge]
UNUserNotificationCenter.current().requestAuthorization(options: permissionsToRequest) { granted, error in
if granted {
print("notification request GRANTED")
}
else {
print("notification request DENIED")
}
if let error = error {
print("Error requesting notifications:\n\(error)")
}
else {
DispatchQueue.main.sync {
self.notificationSettings = type(of:self).getNotificationSettings()!
}
}
}
}
static func getNotificationSettings() -> UNNotificationSettings? {
var settings : UNNotificationSettings?
let start = Date()
let semaphore = DispatchSemaphore(value: 0)
UNUserNotificationCenter.current().getNotificationSettings { notificationSettings in
settings = notificationSettings
semaphore.signal()
}
semaphore.wait()
while settings == nil {
let elapsed = start.distance(to: Date())
Thread.sleep(forTimeInterval: TimeInterval(0.001))
if elapsed > TimeInterval(1) {
print("ERROR: did not get notification settings in less than a second, giving up!")
break
}
}
if settings != nil {
print("\(Date()) Notifications are: \(settings!.authorizationStatus)")
}
return settings
}
}
func getUNAuthorizationStatusString(_ authStatus : UNAuthorizationStatus) -> String {
switch authStatus {
case .notDetermined: return "not determined"
case .denied: return "denied"
case .authorized: return "authorized"
case .provisional: return "provisional"
case .ephemeral: return "ephemeral"
#unknown default: return "unknown case with rawValue \(authStatus.rawValue)"
}
}
extension UNAuthorizationStatus : CustomStringConvertible {
public var description: String {
return getUNAuthorizationStatusString(self)
}
}
extension String.StringInterpolation {
mutating func appendInterpolation(_ authStatus: UNAuthorizationStatus) {
appendLiteral(getUNAuthorizationStatusString(authStatus))
}
}
EDIT: I tried adding objectWillChange but the view still isn't updating.
class TestViewModel : ObservableObject {
let objectWillChange = ObservableObjectPublisher()
#Published var notificationSettings : UNNotificationSettings {
willSet {
objectWillChange.send()
}
}
init() {
notificationSettings = type(of:self).getNotificationSettings()!
}
Per the apple docs the properties wrappers like #Published should hold values. UNNotificationSettings is a reference type. Since the class gets mutated and the pointer never changes, #Publushed has no idea that you changed anything. Either publish a value (it make a struct and init it from he class) or manually send the objectwillChange message manually.
While I was not able to get it to work with manually using objectWillChange, I did create a basic working system as follows. Some functions are not repeated from the question above.
struct TestModel {
var notificationAuthorizationStatus : UNAuthorizationStatus
init() {
notificationAuthorizationStatus = getNotificationSettings()!.authorizationStatus
}
}
class TestViewModel : ObservableObject {
#Published var theModel = TestModel()
func requestAndUpdateNotificationStatus() {
requestNotificationPermissions()
theModel.notificationAuthorizationStatus = getNotificationSettings()!.authorizationStatus
}
}
struct ContentView: View {
#ObservedObject var theViewModel : TestViewModel
var body: some View {
VStack {
Button("Tap to update") {
theViewModel.requestAndUpdateNotificationStatus()
}
.padding()
switch theViewModel.theModel.notificationAuthorizationStatus {
case .notDetermined: Text("Notifications have not been requested yet.")
case .denied: Text("Notifications are denied.")
case .authorized: Text("Notifications are authorized.")
case .provisional: Text("Notifications are provisional.")
case .ephemeral: Text("Notifications are ephemeral.")
#unknown default: Text("Notifications status is an unexpected state.")
}
}
}
}
I have MFi device(RFID reader) and I'm trying to catch scanned tags in my app. So I use EAAccessorymanager to get the shared accessory and read data from it. When I open session and scan tags, stream function does not fire and I can't understand what I'm doing wrong.
import Foundation
import ExternalAccessory
class RFID: NSObject, EAAccessoryDelegate, StreamDelegate {
var _accessory: EAAccessory!
var _session: EASession!
var _protocolString: String?
func turnOn() {
let manager = EAAccessoryManager.shared()
let accessories = manager.connectedAccessories
if (accessories.count > 0) {
_accessory = accessories[0];
_session = EASession(accessory: _accessory, forProtocol: _accessory.protocolStrings[0]);
_accessory.delegate = self;
_session?.inputStream?.delegate = self;
_session?.inputStream?.schedule(in: .current, forMode: .default);
_session?.inputStream?.open();
print(_accessory);
print(_session);
}
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.openCompleted:
break
case Stream.Event.hasBytesAvailable:
// Read Data
print("Data Available");
break
case Stream.Event.hasSpaceAvailable:
// Write Data
break
case Stream.Event.errorOccurred:
break
case Stream.Event.endEncountered:
break
default:
break
}
}
}
NOTE: printed accessory is always right one!
This is my a simplified version of my code:
var myObservable: Observable<MyEnum>
var modelObservable: Observable<Model?>
myObservable = myButton.rx.tap.asSignal()
.asObservable()
.flatMapLatest {
getModel()
}.map { model in
print("this is called")
return model.prop == true ? MyEnum.first : MyEnum.second
}
func getModel() -> Observable<Model?> {
if let model = self.model.value {
return Observable.just(model)
}
createModel()
return modelObservable
}
myObservable.subscribe(onNext: { (enum) in
switch enum {
case .first:
self.presentFirst()
case .second:
self.presentSecond()
}
}).disposed(by: bag)
I was expecting this code to mean that whenever myButton is tapped, this code would run and print "this is called", however, "this is called" is printed also when myOtherObservable is triggered, even when myButton is not tapped. Why does this happen? This makes me think I don't understand Rx. Also, how would I make it behave so that it only runs when the myButton is tapped?
I'm making a permissions system, and the emum below shows the possible permissions for an advance. I get the permissions from the server as a string: advance.answer.view. Is there any way to convert that string into the enum Advance.Answer.View?
enum Advance {
case View, Edit
enum Answer { case View, Edit }
enum Ride { case View, Edit }
enum Contact { case View, Edit }
enum Document { case View, Edit }
enum Guest { case View, Edit }
enum Section { case View, Edit, Create }
enum Member { case View, Edit }
enum Flight { case View, Edit }
enum Location { case View, Edit }
enum Time { case View, Edit }
enum Event { case View, Edit }
}
You need to make the enum a String rawvalue like
enum Answer:String {
case View = "View"
case Edit = "Edit"
init(s:String) {
if s == "xy" { self = .View}
else { self = .Edit }
}
mutating func fromString(s:String) {
if s == "xy" { self = .View}
}
}
var answer1 = Answer(rawValue:"View")!
var answer2 = Answer(s:"x")
answer2.fromString("xy")
You can modify your visual structure into:
enum Advance {
case Answer(view: Bool, edit: Bool)
}
// usage
Advance.Answer(view: false, edit: true)
UPD.
Another case:
enum Advance: String {
case View = "view", Edit = "edit"
static func getEnumType(value: String) -> EnumProtocol.Type {
switch value {
case "contact":
return Contact.self
default:
return Answer.self
}
}
enum Answer: String, EnumProtocol {
init(rawValue: String) {
switch rawValue {
case "edit":
self = .Edit
default:
self = .View
}
}
case View = "view", Edit = "edit"
}
enum Contact: String, EnumProtocol {
init(rawValue: String) {
switch rawValue {
case "write":
self = .Write
case "edit":
self = .Edit
default:
self = .View
}
}
case View = "view", Edit = "edit", Write = "write"
}
}
EnumProtocol you can extend with get function of rawValue.
protocol EnumProtocol {
init(rawValue: String)
}
Update at (31/01/2016)
Usage:
Advance.getEnumType("contact").init(rawValue: "write") // Advance.Contact.Write
Advance.getEnumType("unknown").init(rawValue: "abc") // Advance.Answer.View