Cannot receive event with custom DelegateProxy and Protocol - ios

I try to migrate delegate of DifficultyViewDelegate to observable. This is my DifficultyViewDelegate :
#objc protocol DifficultyViewDelegate: class {
func levelDidIncrease()
func levelDidDecrease()
}
And my DifficultyView :
weak var delegate: DifficultyViewDelegate?
#IBAction func decreaseLevel(_ sender: Any) {
delegate?.levelDidDecrease()
}
#IBAction func increaseLevel(_ sender: Any) {
delegate?.levelDidIncrease()
}
And this is my RxDifficultyViewDelegateProxy
class RxDifficultyViewDelegateProxy: DelegateProxy, DelegateProxyType {
static func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let difficultyView: DifficultyView = object as! DifficultyView
return difficultyView.delegate
}
static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let difficultyView: DifficultyView = object as! DifficultyView
difficultyView.delegate = delegate as? DifficultyViewDelegate
}
}
I also added an extension on my DifficultyView :
extension DifficultyView {
public var rx_delegate: RxDifficultyViewDelegateProxy {
return RxDifficultyViewDelegateProxy.proxyForObject(RxDifficultyViewDelegateProxy.self)
}
public var rx_levelDidIncrease: Observable<Void> {
return rx_delegate.methodInvoked(#selector(DifficultyViewDelegate.levelDidIncrease)).map { _ in return }
}
}
But it seems that when I do :
difficultyView.rx_levelDidIncrease.asObservable().subscribe(onNext: {
print("did increase")
}).addDisposableTo(disposeBag)
It's never called. Someone has any pointers ?

Try use PublishSubject:
DifficultyView:
class DifficultyView: UIView {
var levelDidIncrease = PublishSubject<Void>()
var levelDidDecrease = PublishSubject<Void>()
#IBAction func decreaseLevel(_ sender: Any) {
levelDidDecrease.onNext()
}
#IBAction func increaseLevel(_ sender: Any) {
levelDidIncrease.onNext()
}
}
And then:
var difficultyView = DifficultyView()
difficultyView.levelDidDecrease.asObservable()
.subscribe(onNext: {
print("did decrease")
})
.addDisposableTo(disposeBag)
difficultyView.decreaseLevel(theSender) // <- THIS line performs the side effect

Related

ios Swift Protocol Data

I don't use storyboards.
I want to send protocol data using #objc button action.
However, the sent view controller does not run the protocol function.
May I know what the reason is?
In fact, there's a lot more code.
Others work, but only protocol functions are not executed.
The didUpdataChampion function is
Data imported into a different protocol.
I have confirmed that there is no problem with this.
protocol MyProtocolData {
func protocolData(dataSent: String)
func protocolCount(dataInt: Int)
}
class PickViewController: UIViewController,ChampionManagerDelegate{
static let identifier = "PickViewController"
var count = 0
var urlArray = [URL]()
var pickDelegate : MyProtocolData?
override func viewDidLoad() {
super.viewDidLoad()
champions.riot(url: "myURL")
}
#objc func topHand(){
pickDelegate?.protocolData(dataSent: "top")
print(count)
pickDelegate?.protocoCount(dataInt: count)
let cham = ChampViewController()
cham.modalPresentationStyle = .fullScreen
present(cham, animated: true, completion: nil)
}
//Data imported to another protocol
func didUpdataChampion(_ championManager: ChampionManager, champion: [ChampionRiot]) {
print(#function)
count = champion.count
for data in champion {
let id = data.id
guard let url = URL(string: "https://ddragon.leagueoflegends.com/cdn/11.16.1/img/champion/\(id).png") else { return }
urlArray.append(url)
count = urlArray.count
}
}
func didFailWithError(error: Error) {
print(error)
}
}
class ChampViewController: UIViewController,MyProtocolData {
var pickData = ""
var arrayCount = 0
override func viewDidLoad() {
super.viewDidLoad()
}
func protocolData(dataSent: String) {
print(#function)
pickData = dataSent
print(pickData)
}
func protocoCount(dataInt: Int) {
print(#function)
arrayCount = dataInt
print(arrayCount)
}
}
i don't see full code, for instance how you call bind to topHand(), my advice is:
check that topHand - is called
check that pickDelegate isn't nil inside topHand
Create Object fo your PickViewController class and set its delegate to self.
var yourObj = PickViewController()
override func viewDidLoad() {
super.viewDidLoad()
yourObj.delegate = self
}

Sinch Video calling sound is coming from front speaker

I have implemented sinch video calling in ios swift project i have followed all process given in sinch implementation document https://www.sinch.com/docs/video/ios/#calling. And i am successfully able to implement but i am getting on issue my video sound in coming from front speaker. how can i solve this problem?? Below my code:
var client: SINClient?
var sinCall : SINCall?
Configuring sinch
//MARK: Configuring Sinch Delegate
func configuringSinch(){
//Configuring Client Key
client = Sinch.client(withApplicationKey: Constants.SINCH_APP_KEY, applicationSecret: Constants.SINCH_PRIVATE_KEY, environmentHost: Constants.SANDBOX_ENVIRONMENT, userId: Utility().getUserId())
client?.call().delegate = self
client?.setSupportCalling(true)
client?.enableManagedPushNotifications()
client?.start()
client?.startListeningOnActiveConnection()
let vcCont = client?.videoController()
self.vwLocalView.addSubview((vcCont?.localView())!)
self.sinCall?.delegate = self
}
//MARK: Sinch Video Call Delegate
func clientDidStart(_ client: SINClient!) {
print("Client Did Start")
}
func clientDidFail(_ client: SINClient!, error: Error!) {
print("Client failed : \(error)")
player?.stop()
}
func clientDidStop(_ client: SINClient!) {
print("Client Did Stop")
player?.stop()
}
//MARK: Video Call Did Recieve
func client(_ client: SINCallClient!, didReceiveIncomingCall call: SINCall!) {
print("Did Recieve Incoming Call")
playRingtoneSound() // Playing Audio
call.delegate = self;
self.sinCall = call
}
//MARK: Call Did Add Video Track
func callDidAddVideoTrack(_ call: SINCall!) {
let videoCont = client?.videoController()
vwRemoteView.addSubview((videoCont?.remoteView())!)
}
func callDidEnd(_ call: SINCall!) {
sinCall?.hangup()
}
This is how you can manage SINAudioController to manage audio output.
func audioController() -> SINAudioController {
return (client?.audioController())!
}
//MARK: Video Call Did Recieve
func client(_ client: SINCallClient!, didReceiveIncomingCall call: SINCall!) {
audioController().enableSpeaker()
playRingtoneSound() // Playing Audio
call.delegate = self;
self.sinCall = call
}
// In SINCallDelegate
func callDidEstablish(_ call: SINCall!) {
//to disableSpeaker
audioController().disableSpeaker()
}
try this to manage AudioOutput Session manually
// MARK: AudioOutput Session
// to enable front speaker manually
func setSessionPlayerOn()
{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.none)
} catch _ {
}
}
// to enable speaker manually
func setSessionPlayerSpeaker()
{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
}
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch _ {
}
}
// to turnoff AudioOutput Session manually
func setSessionPlayerOff()
{
do {
try AVAudioSession.sharedInstance().setActive(false)
} catch _ {
}
}
Use this function
func callDidEstablish(_ call: SINCall!) {
let audio = APPDELEGATE.client?.audioController()
audio?.disableSpeaker()
// self.startCallDurationTimerWithSelector()
appDelegate.client?.audioController().stopPlayingSoundFile()
}
this is work for me.
class MainVC: UIViewController,SINCallDelegate {
// var client: SINClient?
private let videoController = SinchManager.sharedInstance.client!.videoController()
private let audioController = SinchManager.sharedInstance.client!.audioController()
private let callClient: SINCallClient
private var call: SINCall!
let username: String
#IBOutlet weak var otherView: UIView!
// private var mainView: SinchView { return view as! SinchView }
#IBAction func call_btn(_ sender: UIButton) {
answer()
}
#IBAction func end_btn(_ sender: UIButton) {
decline()
}
override func loadView() {
// view = SinchView()
view = otherView
}
init(username: String) {
self.username = username
self.callClient = SinchManager.sharedInstance.client!.call()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
print("init(coder:) has not been implemented " + String(describing: aDecoder))
fatalError("init(coder:) has not been implemented " + String(describing: aDecoder))
}
override func viewDidLoad() {
super.viewDidLoad()
call.delegate = self
//self.mainView.videoView.addSubview(self.videoController.localView())
otherView.addSubview((self.videoController?.localView())!)
self.videoController?.localView().contentMode = .scaleToFill
if self.call.direction == SINCallDirection.incoming {
self.audioController?.startPlayingSoundFile(self.pathForSound(string: "incoming.wav") as String, loop: true)
}
if self.call.details.isVideoOffered {
print("video offered")
//self.mainView.videoView.addSubview(self.videoController.localView())
otherView.addSubview((self.videoController?.localView())!)
self.videoController?.localView().contentMode = .scaleToFill
}
otherView.addSubview((self.videoController?.localView())!)
// mainView.answerButton.addTarget(self, action: #selector(answer), forControlEvents: .TouchUpInside)
// mainView.declineButton.addTarget(self, action: #selector(decline), forControlEvents: .TouchUpInside)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.audioController?.enableSpeaker()
}
func pathForSound(string: String) -> NSString {
let nsSt = Bundle.main.resourcePath! as NSString
return nsSt.appendingPathComponent(string) as NSString
}
func answer() {
call.answer()
}
func decline() {
call.hangup()
}
func callDidEstablish(call: SINCall!) {
print("callDidEstablish")
let audio = SinchManager.sharedInstance.client!.audioController()
audio?.disableSpeaker()
// self.startCallDurationTimerWithSelector()
SinchManager.sharedInstance.client!.audioController().stopPlayingSoundFile()
}
func callDidEnd(call: SINCall!) {
print("callDidEnd")
}
func callDidProgress(call: SINCall!) {
print("callDidProgress")
self.audioController?.startPlayingSoundFile(self.pathForSound(string: "ringback.wav") as String, loop: true)
}
func callDidAddVideoTrack(call: SINCall!) {
print("callDidAddVideoTrack")
otherView.addSubview((self.videoController?.localView())!)
}

Add elements to search history?

I have a model - Movies.
and two controllers - first for search movie by title, second - for display result with poster, title and year.
Now i need to create some history search on my third controller
(searchHistoryController - TableView) where displayed all movies, and when i tapped on cell with movie's title show movie info.
How I can build it?
I tried create array in my model. And write resutl in it, but each time when i use search it rewrite array, not add new element.
Maybe use realm
Need some help:)
Movie.swift
import Foundation
import UIKit
import Alamofire
import AlamofireImage
protocol MovieDelegate {
func updateMovieInfo()
}
class Movie {
private let omdbUrl = "http://www.omdbapi.com/?"
var title: String?
var filmYear: String?
var poster: String?
var delegete: MovieDelegate!
var historyMovie = [Movie]()
func getMovieInfo(title: String, completion: #escaping ()->()){
let params = ["t": title]
Alamofire.request(omdbUrl, method: .get, parameters: params).validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
let response = JSON as! NSDictionary
let status = response["Response"] as! String
if status == "True" {
self.title = (response["Title"] as! String)
self.filmYear = (response["Year"] as! String)
self.poster = (response["Year"] as! String)
// self.delegete.updateMovieInfo()
completion()
} else {
self.title = (response["Error"] as! String)
completion()
}
case .failure(let error):
print (error)
}
}
}
}
SearchVC
import UIKit
class SearchViewController: UIViewController {
var movie = Movie()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var searchButton: UIButton!
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
self.performSegue(withIdentifier: "movieInfo", sender: self)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
}
}
DetailVC
class DetailInfoViewController: UIViewController, MovieDelegate {
#IBAction func showHistory(_ sender: UIButton) {
performSegue(withIdentifier: "showHistory", sender: self)
}
#IBOutlet weak var posterImageView: UIImageView!
#IBOutlet weak var filmNameLabel: UILabel!
#IBOutlet weak var filmYearLabel: UILabel!
var movie = Movie()
var movieTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
self.movie.getMovieInfo(title: movieTitle ) {
self.updateMovieInfo()
}
self.movie.delegete = self
}
func updateMovieInfo() {
getPoster(link: movie.poster)
filmNameLabel.text = movie.title
filmYearLabel.text = movie.filmYear
}
func getPoster(link: String?) {
if link != nil {
guard let url = URL(string: link!) else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
self.posterImageView.image = UIImage(data: data)
}
}
} } else {
self.posterImageView.image = #imageLiteral(resourceName: "Image")
}
}
}
First of all, movieHistory should not be part of your Movie class, but part of your SearchViewController class.
Second of all, unless you want to persist your data, you don't need Realm for this.
Just save the movies in SearchViewController into an array once the search button has been tapped and send it to your other view controller in the segue. Like so
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
movieHistory.append(movie)
self.performSegue(withIdentifier: "movieInfo", sender: movieHistory)
})
}
}
Also, modify prepare(for segue:...) like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
secondVC.movieHistory = movieHistory
}
In detailVC override prepare(for segue:...) as well and send movieHistory to searchHistoryController the same way it is done in the previous VC.

Swift closure in protocol extension

I want to Decorate UIViewController with the ability to adjust it's interface when setInteractionEnabled method is called from another class (ex. Network State Manager). All changes (if any) should be provided in the concrete controller by overriding onInteractionChanged. Here is my code:
import Foundation
typealias InteractionClosure = ((enabled: Bool) -> Void)
protocol Interaction: class {
var onInteractionChanged: InteractionClosure? { get set }
func setInteractionEnabled(enabled: Bool)
}
extension Interaction where Self: UIViewController {
// Default: Do nothing
// Throws: - Extensions may not contain stored properties
var onInteractionChanged: InteractionClosure? = nil
func setInteractionEnabled(enabled: Bool) {
onInteractionChanged?(enabled: enabled)
}
}
extension UIViewController : Interaction {}
How to add default implementation for onInteractionChanged?
Answering my own question is something usually I don't do, but here is my solution:
typealias InteractionClosure = (enabled: Bool) -> Void
protocol Interaction: class {
func addOnInteractionChanged(closure: InteractionClosure)
func setInteractionEnabled(enabled: Bool)
}
extension Interaction where Self: UIViewController {
func addOnInteractionChanged(closure: InteractionClosure) {
onInteractionChanged = closure
}
func setInteractionEnabled(enabled: Bool) {
onInteractionChanged?(enabled: enabled)
}
// MARK: - Private
private var onInteractionChanged: InteractionClosure? {
get {
let wrapper =
objc_getAssociatedObject(self, &icAssociationKey) as? ClosureWrapper
return wrapper?.closure
}
set(newValue) {
objc_setAssociatedObject(self,
&icAssociationKey,
ClosureWrapper(newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
}
extension UIViewController : Interaction {}
// Helpers
private var icAssociationKey: UInt8 = 0
private class ClosureWrapper {
var closure: InteractionClosure?
init(_ closure: InteractionClosure?) {
self.closure = closure
}
}
Client class:
class LoginViewController: UIViewController {
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.setup()
}
// MARK: - Private
private func setup() {
// ...
addOnInteractionChanged { [unowned self] (enabled) in
self.signInButton.enabled = enabled
self.activityIndicatorView.hidden = !enabled
}
}
}
In manager class:
visibleViewController?.setInteractionEnabled(true)
If you would like property to have only { get } ability, you can use:
protocol TestProtocol {
var testClosure: ((_ parameter: Bool) -> Void)? { get }
}
extension TestProtocol {
var testClosure: ((_ parameter: Bool) -> Void)? {
return { parameter in
print(parameter)
}
}
}

Firebase adding data to Array but still empty

As you can see here
import UIKit
class Connecting {
let refHandler = Firebase(url: "https://-unique-link-.firebaseio.com/handlers")
var handlerID: [AnyObject] = []
func getHandlerStatus() {
refHandler.queryOrderedByChild("status").observeEventType(.ChildAdded, withBlock: { snapshot in
if let status = snapshot.value["status"] as? Int {
if status == 0 {
self.handlerID.append(snapshot.key)
print(self.handlerID)
} else {
//Do Nothing
}
}
})
}
func handlerIDReturn() -> NSArray {
print(handlerID)
return handlerID
}
}
handlerID array is filled but when i check, its empty. :( please help
Ive updated with all the codes.
Here is a calling code. I use button to call each fund
#IBAction func callerButton(sender: AnyObject) {
Connecting().getHandlerStatus()
}
#IBAction func button2nd(sender: AnyObject) {
print(Connecting().handlerIDReturn())
}
You need to create just one instance of Connecting and use that. At the moment, every call to Connecting() is giving you a new object.
let connecting = Connecting()
#IBAction func callerButton(sender: AnyObject) {
connecting.getHandlerStatus()
}
#IBAction func button2nd(sender: AnyObject) {
print(connecting.handlerIDReturn())
}

Resources