I am learning & developing ios application with swift.I have 2 tabs in my app,so I have 2 view controller.I need to connect socket server.But in which file ?
First tab is showing conversations list and second tab is chat interface.User is sending message from second tab.If someone sends a message to this user,I need to show this message in first tab.
I need to connect socket server but in which file ? I mean example: When message arrives to this user i need to save it to database and show user in second tab.Is view controller file good for this case ?
I would recommend checking out Alamofire. It is a fantastic networking library built entirely for Swift and has really picked up in popularity of the past few months. This makes it incredibly easy to call a web service to fetch data.
WebSockets
If you actually need to connect to your server using web sockets, then I'd check out SocketRocket built by the fine folks at Square. Here's a link to their project on Github.
Since you're new to iOS development, I'd suggest a simple architecture where you abstract the network calls out of your view controllers.
ChatManager
Internally manages all networking such as SocketRocket
Should be a singleton or property on UIApplicationDelegate
Has public API for sending message
Sends out notifications when it receives notifications back
class ChatManager {
// Add property for socket
class var sharedInstance: ChatManager {
struct Singleton { static let instance = ChatManager() }
return Singleton.instance
}
init() {
// Create the socket
}
func sendMessage(message: String) {
// Push the message onto the socket
}
// Delegate methods
func messageReceived(message: String) {
// Emit the message using NSNotificationCenter
}
}
View Controller 1
Subscribes to notifications from ChatManager
class ViewController1 : UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Register for NSNotification coming from ChatManager
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
View Controller 2
Subscribes to notifications from ChatManager
Sends new messages to the ChatManager to push through the socket
class ViewController2 : UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Register for NSNotification coming from ChatManager
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func userAddedNewChatMessage(message: String) {
ChatManager.sharedInstance.sendMessage(message)
}
}
Related
I am using VIPER architecture in iOS swift. I have 2 viewcontrollers, let's say A and B. At first I'm going from A to B, performing some tasks and coming back from B to A. With MVC or MVVM, we can create protocols and transfer data from B to A. But for VIPER, i am confused. Here's my VIPER code B, while Back button is tapped:
View:
#IBAction func backButtonTapped(_ sender: UIButton) {
presenter?.goBack()
}
Presenter:
func goBack() {
router.back()
}
Router:
func back() {
viewController?.navigationController?.popViewController(animated: true)
//here I want to send data back to previous viewcontroller
}
I have tried creating one method in the Router of previous controller and sending the data through that method but it is not working as router doesn't have any instance of presenter or anything else, except view.
Note:- In Viper Router is Unidirectional.
So this might help you.
Implement ProtocolDelegate in Current Module's VC
Create a delegate variable in the Next Module's Router
Then Simply Send Delegate Dependancy to Next Module's Router
And Call your delegate method from Next Module's Router.
Module A
final class VCA: SomeProtocolDelegate {
fund someMethod() {
//Your task Action From Module B
}
}
final class ModuleARouter: WireFrameProtocol {
fund gotoModuleB(withView vc: VCA) {
ModuleBRouter.load(onView: vc)
}
}
Module B
final class ModuleBRouter: WireframeProtocol {
internal weak var delegate: SomeProtocolDelegate?
// Here you can add more argument on load method for delegate
// since in this example i'm send data back ViewController So I didn't create
class func load(onView VC: UIViewController) {
//setup your VIPER protocol and class
if let vCA = VC as? VCA {
router.delegate = vCA
}
}
func backToPreviousModule() {
self.delegate?. someMethod()
}
}
I want to call a web service whenever application coming back in the foreground. I am calling it from didBecomeActive().
What's the best way to handle it and pass data to Root view controller?
Since the data you want to pass is always going to the same view controller you should instead set the observer in that view controller instead of app delegate. This way you won't need to pass any data in the first place.
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default
.addObserver(self, selector: #selector(activityHandler(_:)),
name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func activityHandler(_ notification: Notification) {
//Call your web service here
}
}
You have two choices. Get rootViewController and pass the data, handle it.
func applicationDidBecomeActive(_ application: UIApplication) {
// 1
let rootVC1 = self.window?.rootViewController
// 2
let rooVC2 = application.windows.first?.rootViewController
...
/*
pass data to rootVC1 or rootVC2
*/
}
I have simple ListView that needs to display the records from the network layer. (first screen of the application)
I need to get some opinion as to which will be correct flow so that I can make the unit test cases easily. (No VIPER architecture)
NetworkMgr makes the network calls and create Model objects.
These Model objects needs to be populated in ListTableView.
I have a completion handler method to call the network request which give the model objects.
func getData() {
dataMgr.requestData(url: "String") { (EmployeesArray, error) in
// print(error)
}
}
Now the Question is - For unit testing when I am calling the ListDataTest since the ListVC is in storyboard when it loads the View the viewdidLoad method calls the which will initiate the network logic.
SO I am not able to test only the UI related stuffs.
I tried to create some extension in ListDataTest class but no success is achieved.
Below is the flow of the Controllers : -
===
class ListVC: UIViewController {
var dataProvider: ListData
override func viewDidLoad() {
super.viewDidLoad()
dataProvider.viewLoaded()
}
}
=======
In ListData class
protocol DatProviderLoad {
func viewLoaded()
}
class ListData: NSObject {
}
extension ListData : DatProviderLoad {
func viewLoaded() {
print("loaded")
//the network calls goes here
}
}
/—
The test class
class ListDataProviderTest: XCTestCase {
var sut: ListData!
var controller: ListVC!
override func setUp() {
super.setUp()
sut = ListData()
let storyBoard = UIStoryboard(name:"Main", bundle: nil)
controller = storyBoard.instantiateViewController(withIdentifier: "ListVC") as! ListVC
controller.dataProvider = sut //before this called the storyboard already called the viewdidload once
_ = controller.view
}
}
Your feedback will be very helpful.
Any hint or tutorial in right direction will be highly appreciable.
Lets try to do this in MVVM way.
Think ViewController as part of View layer.
To call the network layer and to convert models into view models introduce a ViewManager.
The Viewcontroller will ask ViewManager to provide the data(ViewModel) and passes all actions(like button press) to ViewManager to handle the business logic.
This way it will be easy to write test cases for ViewManager layer(which is supposed to have all the business logic) and your View is not coupled with either the Network layer or data models.
I have a very basic user class, who os responsible for get user data from Firebase and update the currently screen if needed, everything was working until a decided to update my project to Swift 3.0
This is the User Class
#objc protocol userClassProtocol {
func updateScreen()
#objc optional func sucessUnlockedCategory()
}
class User {
static let sharedInstance = User()
internal var delegate : userClassProtocol?
func fakeClassThatGetsDataFromFirebase() {
//Got data
print("Has new data")
self.delegate?.updateScreen()
}
}
And here is the ViewController:
class FirstViewController: UIViewController, userClassProtocol {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("View will load")
User.sharedInstance.delegate = self
}
func updateScreen() {
print("Screen is going to update")
//Do stuff here
}
}
The logs i get from this are:
Has new Data
View Will Appear
But the function updateScreen() in the view controller never gets called. No errors are being pointed out by Xcode.
Looks like it's just an issue with the timing of your method calls.
Your fakeClassThatGetsDataFromFirebase method is called first, and at that point, your delegate hasn't been set. So self.delegate is nil when calling:
self.delegate?.updateScreen()
This will only work if your viewWillAppear gets called before fakeClassThatGetsDataFromFirebase
It shouldn't get called, according to your code. Nothing calls fakeClassThatGetsDataFromFirebase(), which is what should call updateScreen() on your delegate method.
I'm working with Intercom's iOS SDK and reading through the Header available I found:
//=========================================================================================================
/*! #name Intercom Notifications */
//=========================================================================================================
/*!
These are notifications thrown by Intercom for iOS when the Intercom window is displayed and hidden or when
a new conversation has been started. These notifications are fired only when there is a change in the state
of Intercom's UI: when a user receives a message for instance, willShow and didShow notifications will be
fired accordingly when the Intercom Notification (chat head) is presented.
Once the user taps on the chat head, the message is presented in your app. It will be presented covering
the entire screen, but no notifications will be thrown here as Intercom has already been visible.
In the case of a new conversation this notification may be used to prompt users to enable push notifications.
*/
let IntercomWindowWillShowNotification: String
let IntercomWindowDidShowNotification: String
let IntercomWindowWillHideNotification: String
let IntercomWindowDidHideNotification: String
let IntercomDidStartNewConversationNotification: String
How can I got about catching these notifications in a controller and then do something based on the specific notification that is thrown?
You will need to add your ViewControllerClass as an observer to each notification it should observe in viewDidLoad...
class ITC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("icWindowWillShowNotification:"), name: IntercomWindowWillShowNotification, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func icWindowWillShowNotification (notification: NSNotification) {
//Do something
}
}