Segue called twice - IOS - ios

I am using from VIP in my project and when I route user to another scene open the scene twice ,my router is like bellow? if your need more detail say me. Thank's
#objc protocol ListLanguageRoutingLogic
{
func routeToStartPage(segue: UIStoryboardSegue?)
}
protocol LangSelectedDataPassing
{
var dataStore: SelectLanguageDataStore? { get }
}
class RouterSelectLanguage: NSObject, ListLanguageRoutingLogic, LangSelectedDataPassing
{
weak var viewControllerSelectLanguage: ViewControllerSelectLanguage?
var dataStore: SelectLanguageDataStore?
func routeToStartPage(segue: UIStoryboardSegue?) {
print("BBB")
let destinationVC = viewControllerSelectLanguage?.storyboard?.instantiateViewController(withIdentifier: "ViewControllerStartPage") as! ViewControllerStartPage
var destinationDS = destinationVC.router!.dataStore!
passDataToStartPage(source: dataStore!, destination: &destinationDS)
navigateToStartPage(source: viewControllerSelectLanguage!, destination: destinationVC)
}
// MARK: Navigation
func navigateToStartPage(source: ViewControllerSelectLanguage, destination: ViewControllerStartPage)
{
source.show(destination, sender: nil)
}
// MARK: Passing data
func passDataToStartPage(source: SelectLanguageDataStore, destination: inout StartPageDataStore)
{
print("CCC")
let selectedRow = viewControllerSelectLanguage?.tblView.indexPathForSelectedRow?.row
destination.langSelected = source.langs?[selectedRow!]
}
}
And:

Resolved my problem, I edited my source code like bellow and good work now:
#objc protocol ListLanguageRoutingLogic
{
func routeToStartPage(segue: UIStoryboardSegue?)
}
protocol LangSelectedDataPassing
{
var dataStore: SelectLanguageDataStore? { get }
}
class RouterSelectLanguage: NSObject, ListLanguageRoutingLogic, LangSelectedDataPassing
{
weak var viewControllerSL: ViewControllerSelectLanguage?
var dataStore: SelectLanguageDataStore?
func routeToStartPage(segue: UIStoryboardSegue?) {
let destinationVC = segue!.destination as! ViewControllerStartPage
var destinationDS = destinationVC.startPageRouter!.dataStore!
passDataToStartPage(source: dataStore!, destination: &destinationDS)
}
// MARK: Passing data
func passDataToStartPage(source: SelectLanguageDataStore, destination: inout StartPageDataStore)
{
let selectedRow = viewControllerSL?.tblView.indexPathForSelectedRow?.row
destination.langSelected = source.langs?[selectedRow!]
}
}

Related

Cant pass data between view controllers using segue

I am trying to get my app to call to an API, retrieve information about a movie based on a user search, and then pass that data to the next view using segue. Everything seems to be working fine up until the point where the data is supposed to be getting transferred over. My two view controllers and the file where i call my API are below.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
var movieManager = MovieManager()
var movieTitle = ""
var movieDescription = ""
var results: [Results] = []
override func viewDidLoad() {
movieManager.delegate = self
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
//MARK: - Movie Manager Methods
extension ViewController: MovieManagerDelegate {
func didUpdateMovie(title: String?, description: String?, resultsList: [Results]?) {
movieTitle = title!
movieDescription = description!
results = resultsList!
print("\(movieTitle) \n \(movieDescription)")
}
func didFailWithError(error: Error) {
print("error")
}
}
//MARK: - Search Bar Methods
extension ViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
movieManager.searchMovie(for: searchBar.text!)
searchBar.text = ""
searchBar.resignFirstResponder()
performSegue(withIdentifier: "goToResults", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResults" {
if let destinationVC = segue.destination as? ResultsListViewController {
destinationVC.movieTitle = self.movieTitle
}
}
}
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
searchBar.resignFirstResponder()
return true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
import UIKit
class ResultsListViewController: UIViewController {
#IBOutlet weak var movieName: UILabel!
var movieTitle: String = ""
override func viewDidLoad() {
super.viewDidLoad()
movieName.text = movieTitle
}
}import UIKit
class ResultsListViewController: UIViewController {
#IBOutlet weak var movieName: UILabel!
var movieTitle: String = ""
override func viewDidLoad() {
super.viewDidLoad()
movieName.text = movieTitle
}
}
import Foundation
protocol MovieManagerDelegate {
func didUpdateMovie(title: String?, description: String?, resultsList: [Results]?)
func didFailWithError(error: Error)
}
struct MovieManager {
var movieTitle: String?
var movieDescription: String?
var results: [Results]?
var delegate: MovieManagerDelegate?
let baseURL = "https://imdb-api.com/en/API/SearchMovie/(APIKEYHERE)/"
func searchMovie(for userSearch: String){
let urlString = "\(baseURL)\(userSearch)"
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
let movieInfo = self.parseJSON(safeData)
self.delegate?.didUpdateMovie(title: movieInfo.0!, description: movieInfo.1!, resultsList: movieInfo.2)
}
}
task.resume()
}
}
func parseJSON(_ data: Data) -> (String?, String?, [Results]) {
let decoder = JSONDecoder()
var movieTitle = "", movieDescription = "", searchResults: [Results] = []
do {
if let decodedData = try decoder.decode(MovieData?.self, from: data) {
movieTitle = decodedData.results.first!.title
movieDescription = decodedData.results.first!.description
searchResults = decodedData.results
// print("\(movieTitle) \n \(movieDescription)")
}
return (movieTitle, movieDescription, searchResults)
} catch {
print(error)
}
return (movieTitle, movieDescription, searchResults)
}
}
Problem is Segue to next controller before the result of API.
Try to Segue after API result
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
movieManager.searchMovie(for: searchBar.text!)
searchBar.text = ""
searchBar.resignFirstResponder()
//Segue to next controller before the result of API
performSegue(withIdentifier: "goToResults", sender: self)
}
So your segue to next controller would be after result of parseJSON or didUpdate delegate

What am I doing wrong on passing data through protocol

I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!

RxSwift, MVVM: Communication with child view controller

I'm trying to follow https://github.com/sergdort/CleanArchitectureRxSwift building an app with RxSwift and MVVM.
I can't wrap my head around how to communicate with a child view controller in this scenario.
I have one trigger from the ParentViewController that is needed in the ChildViewModel and a second one from the ChildViewModel that should trigger some UI changes in the ParentViewController:
trigger1: ParentViewController (input)> ... (no idea)> ChildViewModel
trigger2: ChildViewModel (output)> ... (no idea)> ParentViewController
My code currently looks something like this:
final class ParentViewController: UIViewController {
var viewModel: ParentViewModel?
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
private func bindViewModel() {
guard let viewModel = self.viewModel else {
fatalError("View Model not set!")
}
let triggerToChild = rx.someTrigger
let input = ParentViewModel.Input(triggerToChild: triggerToChild)
let output = viewModel.transform(input: input)
output.triggerFromChild
.drive(rx.someProperty)
.disposed(by: disposeBag)
}
}
final class ParentViewModel: ViewModelType {
struct Input {
let triggerToChild: Driver<Void>
}
struct Output {
let triggerFromChild: Driver<Void>
}
func transform(input: Input) -> Output {
let triggerFromChild = ??? <===================
return Output(triggerFromChild: triggerFromChild)
}
}
final class ChildViewController: UIViewController {
var viewModel: ChildViewModel?
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
private func bindViewModel() {
guard let viewModel = self.viewModel else {
fatalError("View Model not set!")
}
let triggerFromParent = ??? <===================
let input = ChildViewModel.Input(triggerFromParent: triggerFromParent)
let output = viewModel.transform(input: input)
output.triggerFromParent
.drive(rx.someProperty)
.disposed(by: disposeBag)
}
}
final class ChildViewModel: ViewModelType {
struct Input {
let triggerFromParent: Driver<Void>
}
struct Output {
let triggerToParent: Driver<Void>
}
func transform(input: Input) -> Output {
let triggerToParent = rx.someTrigger
return Output(triggerToParent: triggerToParent)
}
}
Maybe someone could point me in the right direction? Thank You!

How to implement simple MVC design pattern in Swift?

I am new to MVC design pattern. I created "DataModel" it will make an API call, create data, and return data to the ViewController using Delegation and "DataModelItem" that will hold all data. How to call a DataModel init function in "requestData" function. Here is my code:
protocol DataModelDelegate:class {
func didRecieveDataUpdata(data:[DataModelItem])
func didFailUpdateWithError(error:Error)
}
class DataModel: NSObject {
weak var delegate : DataModelDelegate?
func requestData() {
}
private func setDataWithResponse(response:[AnyObject]){
var data = [DataModelItem]()
for item in response{
if let tableViewModel = DataModelItem(data: item as? [String : String]){
data.append(tableViewModel)
}
}
delegate?.didRecieveDataUpdata(data: data)
}
}
And for DataModelItem:
class DataModelItem{
var name:String?
var id:String?
init?(data:[String:String]?) {
if let data = data, let serviceName = data["name"] , let serviceId = data["id"] {
self.name = serviceName
self.id = serviceId
}
else{
return nil
}
}
}
Controller:
class ViewController: UIViewController {
private let dataSource = DataModel()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
dataSource.requestData()
}
}
extension ViewController : DataModelDelegate{
func didRecieveDataUpdata(data: [DataModelItem]) {
print(data)
}
func didFailUpdateWithError(error: Error) {
print("error: \(error.localizedDescription)")
}
}
How to implement simple MVC design pattern in Swift?
As a generic answer, in iOS development you're already doing this implicitly! Dealing with storyboard(s) implies the view layer and controlling the logic of how they work and how they are connected to the model is done by creating view controller, that's the default flow.
For your case, let's clarify a point which is: according to the standard MVC, by default the responsible layer for calling an api should be -logically- the view controller. However for the purpose of modularity, reusability and avoiding to create massive view controllers we can follow the approach that you are imitate, that doesn't mean that its the model responsibility, we can consider it a secondary helper layer (MVC-N for instance), which means (based on your code) is DataModel is not a model, its a "networking" layer and DataModelItem is the actual model.
How to call a DataModel init function in "requestData" function
It seems to me that it doesn't make scene. What do you need instead is an instance from DataModel therefore you could call the desired method.
In the view controller:
let object = DataModel()
object.delegate = self // if you want to handle it in the view controller itself
object.requestData()
I am just sharing my answer here and I am using a codable. It will be useful for anyone:
Model:
import Foundation
struct DataModelItem: Codable{
struct Result : Codable {
let icon : String?
let name : String?
let rating : Float?
let userRatingsTotal : Int?
let vicinity : String?
enum CodingKeys: String, CodingKey {
case icon = "icon"
case name = "name"
case rating = "rating"
case userRatingsTotal = "user_ratings_total"
case vicinity = "vicinity"
}
}
let results : [Result]?
}
NetWork Layer :
import UIKit
protocol DataModelDelegate:class {
func didRecieveDataUpdata(data:[String])
func didFailUpdateWithError(error:Error)
}
class DataModel: NSObject {
weak var delegate : DataModelDelegate?
var theatreNameArray = [String]()
var theatreVicinityArray = [String]()
var theatreiconArray = [String]()
func requestData() {
Service.sharedInstance.getClassList { (response, error) in
if error != nil {
self.delegate?.didFailUpdateWithError(error: error!)
} else if let response = response{
self.setDataWithResponse(response: response as [DataModelItem])
}
}
}
private func setDataWithResponse(response:[DataModelItem]){
for i in response[0].results!{
self.theatreNameArray.append(i.name!)
self.theatreVicinityArray.append(i.vicinity!)
self.theatreiconArray.append(i.icon!)
}
delegate?.didRecieveDataUpdata(data: theatreNameArray)
print("TheatreName------------------------->\(self.theatreNameArray)")
print("TheatreVicinity------------------------->\(self.theatreVicinityArray)")
print("Theatreicon------------------------->\(self.theatreiconArray)")
}
}
Controller :
class ViewController: UIViewController {
private let dataSource = DataModel()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
dataSource.requestData()
}
}
extension ViewController : DataModelDelegate{
func didRecieveDataUpdata(data: [DataModelItem]) {
print(data)
}
func didFailUpdateWithError(error: Error) {
print("error: \(error.localizedDescription)")
}
}
APIManager :
class Service : NSObject{
static let sharedInstance = Service()
func getClassList(completion: (([DataModelItem]?, NSError?) -> Void)?) {
guard let gitUrl = URL(string: "") else { return }
URLSession.shared.dataTask(with: gitUrl) { (data, response
, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(DataModelItem.self, from: data)
completion!([gitData],nil)
} catch let err {
print("Err", err)
completion!(nil,err as NSError)
}
}.resume()
}
}
I would recommend using a singleton instance for DataModel, since this would be a class you would be invoking from many points in your application.
You may refer its documentation at :
Managing Shared resources using singleton
With this you wont need to initialise this class instance every time you need to access data.

Swift 3 Delegate function not called

I'm trying to get my head around Swift delegates and stole/knocked-up a Playground but cannot seem to get the delegate function to be called.
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
weak var delegate: fBookDelegate?
init() {
print("initialising fBook")
delegate?.processData(data: "hello world")
print("we should have printed")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook()
getfBook.delegate = self
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
Can anybody spot my mistake ?
All I get for output is
initialising fMain
initialising fBook
we should have printed
all done let's rumble
You can use like this :
import UIKit
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
init(delegate: fBookDelegate?) {
print("initialising fBook")
delegate?.processData(data: "hello world")
print("we should have printed")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook(delegate: self)
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
output :
initialising fMain
initialising fBook
processing data from fBook with hello world
we should have printed
all done let's rumble
Here's one option for using your delegate:
protocol fBookDelegate:class {
func processData(data: String)
}
class fBook {
weak var delegate: fBookDelegate?
init() {
print("initialising fBook")
}
func talkToMe() {
delegate?.processData(data: "hello world")
}
}
class fMain: fBookDelegate {
init() {
print("initialising fMain")
let getfBook = fBook()
getfBook.delegate = self
getfBook.talkToMe()
print("all done let's rumble")
}
func processData(data: String) {
print("processing data from fBook with \(data)")
}
}
var controller = fMain()
Another would be a custom init method that takes the delegate as a parameter.

Resources