I have a viewcontroller class ViewController with collectionView. Also I have singleton class FacebookManager for fetching data from facebook.
What I want to do is to run a method in facebook class and then call a method in ViewController to reload collectionView.
I tried to make a reference to ViewController in Facebook manager by setting
class FacebookManager {
static let sharedInstance = FacebookManager()
var vc:ViewController?
}
Then setting in ViewController
class ViewController: {
func viewDidLoad() {
FacebookManager.sharedInstance.vc = self
}
}
And then calling in FacebookManager
func myMethod() {
vc.collectionView.reloadData()
}
But this doesn't work.
How to do this properly?
To provide communication between two or multiple classes there are two method that are recommended.
1) Delegation
2) Notification
In your given code to implement delegation method we have to create protocol and delegate property in FacebookManager. And assign view controller as a delegate to FacebookManger
Example:
protocol FacebookManagerDelegate: class {
func refreshData()
}
class FacebookManager {
var weak delegate:FacebookManagerDelegate?
func myMethod() {
delegate?.refreshData()
}
}
class ViewController: FacebookManagerDelegate {
ViewDidLoad() {
FacebookManager.sharedInstance.delegate = self
}
func refreshData() {
self.collectionView.reloadData()
}
}
But you are using singleton class therefore in future multiple class would be using this class and if you want to notify multiple classes use Notification method, which pretty easy to implement and should be use in singleton class
Post notification in FacebookManger whenever you want to notify:
class FacebookManager {
func myMethod() {
NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)
}
}
class ViewController {
ViewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(reloadData(_:)), name: NotificationName, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func reloadData(notification: NSNotification) {
self.collectionView.reloadData()
}
}
Related
Ideally, I want to create a BaseViewController class that takes in a protocol type (of a delegate) and have a weak variable as the delegate. Something like this:
class BaseViewController<Delegate: AnyObject> {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
}
}
And then inherit from a view controller like so:
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class SomeViewController: BaseViewController<MyDelegate> {
func doSomething() {
delegate?.funcA()
}
}
This doesn't work as the compiler complains:
'BaseViewController' requires that 'MyDelegate' be a class type
How can I work this around to achieve what I need?
Thanks in advance :)
Thats because in swift protocols doesn't confirm to them selves, you can't use "MyProtocol" as concrete type confirming to protocol "MyDelegate"
What you can rather do is
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class BaseViewController<Delegate: MyDelegate> {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
//keeping OPs code as is
}
}
class SomeOtherDelegateClass: MyDelegate {
func funcA() {
//some code here
}
func funcB() {
//some code here
}
}
class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
func doSomething() {
self.delegate?.funcA()
}
}
EDIT 1:
As OP mentioned in comment, he is trying to introduce a generic property in BaseViewController that will simply hold a weak reference to any instance whose class is decided/declared by Child classes of BaseViewController using generics, I am simplifying the above answer a bit
Try this
protocol MyDelegate {
func funcA()
func funcB()
}
class BaseViewController<Delegate> where Delegate: AnyObject {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
//keeping OPs code as is
}
}
class SomeOtherDelegateClass: MyDelegate {
func funcA() {
//some code here
}
func funcB() {
//some code here
}
}
class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
func doSomething() {
self.delegate?.funcA()
}
}
protocol MyDelegate2 {
func funcABCD()
}
class SomeOtherDelegateClass2: MyDelegate2 {
func funcABCD() {
//some code here
}
}
class SomeViewController2: BaseViewController<SomeOtherDelegateClass2> {
func doSomething() {
self.delegate?.funcABCD()
}
}
TBH, I really dont see much of benefit of this design! Probably you need to revisit the code structure and see if you can come up with better code structure :)
You should set your delegate as a constraint for the generic type T in BaseViewController:
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class Delegated1: MyDelegate {
func funcA() { print("A1") }
func funcB() {}
}
class Delegated2: MyDelegate {
func funcA() { print("A2") }
func funcB() {}
}
class BaseViewController<T: MyDelegate>: UIViewController {
var delegate: T?
func doSomething() {
delegate?.funcA()
}
}
class SomeViewController1: BaseViewController<Delegated1> {}
class SomeViewController2: BaseViewController<Delegated2> {}
class TestClass {
let viewController1: SomeViewController1 = {
let viewController = SomeViewController1(nibName: nil, bundle: nil)
viewController.delegate = .init()
return viewController
}()
let viewController2: SomeViewController2 = {
let viewController = SomeViewController2(nibName: nil, bundle: nil)
viewController.delegate = .init()
return viewController
}()
// prints:
// A1
// A2
func myFunc() {
viewController1.doSomething()
viewController2.doSomething()
}
}
I have a functionality throughout my application. Now i need to call a specific function inside one view controller for some particular condition. So i tried to call, but it fails
main.swift
CommandLine.unsafeArgv.withMemoryRebound(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))
{ argv in
_ = UIApplicationMain(CommandLine.argc, argv, NSStringFromClass(MyClass.self), NSStringFromClass(AppDelegate.self))
}
MyClass.swift
class MyClass: UIApplication {
func resetIdleTimer() {
}
}
MyViewController.swift
class MyViewController: UIViewController {
MyClass.resetIdleTimer(); //this causes error in
}
I need to call resetIdleTimer() inside MyViewController?
Check this code, You can call it from anywhere.
if let obj = MyClass.shared as? MyClass {
obj.resetIdleTimer()
}
For ex. You can call in view controller
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let obj = MyClass.shared as? MyClass {
obj.resetIdleTimer()
}
}
}
I'm practising how to communicate between two view controllers using protocol and delegate (in the xCode background even when I'm using protocol in my project I get the same problem, Delegate is nil), but the problem after setting everything it shows me that my delegate is nil and the sender VC does not send any data since the delegate is nil.
I have confirmed to the protocol and I have set the receiver VC as the delegate, but still can not see where the problem is.
The Protocol
protocol theCommunicationsStructionProtocol{
func dataToTransmit(Data: String)
}
The Sender VC
class TheSenderVC{
var delegate: theCommunicationsStructionProtocol?
func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
}
The Receiver VC
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
TheSenderVCObj.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
calling delegateLuncher() to set the delegate in the receiver VC
TheSenderVC().lookingForDelegate()
calling lookingForDelegate() from the sender VC to look for the
delegate and send it the data
TheReceiverVc1().delegateLuncher()
Note: I have tried accessing the delegate from the receiver VC using this way:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj: TheSenderVC?
func delegateLuncher(){
self.TheSenderVCObj?.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
but I still getting the same problem:
delegate is nil
Finally, I found the solution!
the problem is I was making instances of the TheSenderVC, instead of takling to the original TheSenderVC.
when I was making an object (an instance) of TheSenderVC the problem occurred! instead I have to access the original TheSenderVC, which means I have to use static :)
here is the old delegate set up
var delegate: theCommunicationsStructionProtocol?
from TheSenderVC
here is the new delegate set up
static var delegate: theCommunicationsStructionProtocol?
therefore the
func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
will be changed to
static func lookingForDelegate(){
self.delegate?.dataToTransmit(Data: "Data has been sent")
}
since now it includes a static property (delegate)
on the other hand, the The ReceiverVC1 should be changed from:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
TheSenderVCObj.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
to:
class TheReceiverVc1: theCommunicationsStructionProtocol{
func delegateLuncher(){
TheSenderVC.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
accessing the delegate from the original TheSenderVC()
Where are you create the reference of TheSenderVCObj
replace var TheSenderVCObj: TheSenderVC? to var TheSenderVCObj = TheSenderVC()
let try below code:
class TheReceiverVc1: theCommunicationsStructionProtocol{
var TheSenderVCObj = TheSenderVC()
func delegateLuncher(){
self.TheSenderVCObj?.delegate = self
}
func dataToTransmit(Data: String) {
print("from VC1: \(Data)")
}
}
your TheSenderVCObj also nil according to your code.
NOTE: use proper naming conventions.
Because the TheReceiverVc1 was automatic deinit by ARC.
You need to save reference of the instance like that's:
class ViewController: UIViewController {
let theReceiverVc1: TheReceiverVc1 = TheReceiverVc1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
theReceiverVc1.delegateLuncher()
}
}
Also make sure when you using delegate set it as weak var:
weak var delegate: theCommunicationsStructionProtocol?
I passed data from ViewController1 to ViewController2 via segue, but how can I send data to the Class? This class is not a ViewController.
ViewController1 has a UIPickerView that gives the data (String).
The String will complete an URL needed in ViewController2.
Class
class A: SendDataFromDelegate {
func sendData(data: String) {
self.data = data
}
var delegate : SendDataFromDelegate?
ViewController1
#IBAction func Picker(_ sender: Any) {
var delegate: SendDataFromDelegate?
delegate?.sendData(data: data)
}
protocol SendDataFromDelegate {
func sendData(data : String)
}
Is this a good way to do it?
Or should I create all the possible URLs in the class, and call them from ViewController2?
You should create a protocol with delegate functions like this:
protocol ClassDelegate: class {
func doSomething()
}
In your class A you should implement that protocol this way:
class A: ClassDelegate {
inAFunction(){
ViewController1.delegate = self
}
func sendData(data: String) {
self.data = data
}
}
In the viewController you want to send data to your Class A you should have reference of your class A and use the delegate variable:
class ViewController1: UIViewController {
weak var delegate: ClassDelegate?
func clickHere(){
delegate.sendData()
}
}
When you use clickHere function it triggers Class C sendData function.
Try it ;D
I am writing my iOS Application in Swift 3.
I have a UIViewController extension, where I have to check if the controller instance responds to a method. Below is the code that I a trying out.
extension UIViewController {
func myMethod() {
if self.responds(to: #selector(someMethod)) {
}
}}
Here the responds(to:) method throws a compile time error
Use of unresolved identifier "someMethod".
I read in another post, we have to use self inside the selector argument, but even that is throwing some error.
A simple workaround:
#objc protocol SomeMethodType {
func someMethod()
}
extension UIViewController {
func myMethod() {
if self.responds(to: #selector(SomeMethodType.someMethod)) {
//...
self.perform(#selector(SomeMethodType.someMethod))
// or
(self as AnyObject).someMethod?()
//...
}
}
}
A little more Swifty way:
protocol SomeMethodType {
func someMethod()
}
//For all classes implementing `someMethod()`.
extension MyViewController: SomeMethodType {}
//...
extension UIViewController {
func myMethod() {
if let someMethodSelf = self as? SomeMethodType {
//...
someMethodSelf.someMethod()
//...
}
}
}
Create a protocol which requires someMethod()
protocol Respondable {
func someMethod()
}
And a protocol extension which affects only UIViewController instances
extension Respondable where Self : UIViewController {
func myMethod() {
someMethod()
}
}
Adopt the protocol to some of the view controllers
class VC1 : UIViewController, Respondable {
func someMethod() { print("Hello") }
}
class VC2 : UIViewController {}
class VC3 : UIViewController {}
Now call the method in the extension
let vc1 = VC1()
vc1.myMethod() // "Hello"
Otherwise you get a compiler error:
let vc3 = VC3()
vc3.myMethod() // error: value of type 'VC3' has no member 'myMethod'
Swift 4 answer:
If the selector is written as a string you won't get that error.
extension UIViewController {
func myMethod() {
if self.responds(to: "someMethod")) {
}
}
}
And then in the viewcontroller (dont forget the #objc):
#objc func someMethod() -> Void {}