2

Lately I have been working on a new app and I'm struggling to pass data between view controllers.

I use a view controller which holds a container view in which I use google maps. As some of you know it's impossible to put a button on google maps as it overlays everything (that why I put it as a container view). Now I got a problem that I can't make an action button that can performSegue and I also failed to pass an object with the NotificationCenter. I made a model called Song and I want to pass a song List to other controller (a table view).

class Song {
var sid: String
var songName: String
var artistId: String
var length: String
var songImage: String?
var album: String
var lastUpdate:Date?
}

Any ideas or suggestions to move this list between VCs? There is no real connection between those views though, the mainVC is holding them both as containers.

ntrch
  • 76
  • 1
  • 22
Tubul
  • 21
  • 1
  • Possible duplicate of [Passing Data between View Controllers](https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers) – Tamás Sengel Jan 12 '18 at 14:21
  • I'm looking for a solution in swift that does not include segues. sadly this information doesn't exist in this article you referenced me to. the segue is great but google maps just made it impossible or Im missing something ? – Tubul Jan 12 '18 at 15:35

1 Answers1

0

So you have MainController that own both of your controllers that had to exchange data? On way is to use Delegate pattern and setup weak relationsships between your controllers:

protocol SongSelectable {
    func songSelected(_ song: Song) 
}

class PlaylistController: UIViewController {
    weak var songSelectable: SongSelectable?
}

class MapController: UIViewController, SongSelectable {
    func songSelected(_ song: Song)  {
        //do your code
    }
}

class MainController: UIViewController {
   weak var mapController: MapController?
   weak var playlistController: PlaylistController?
   func prepare(for segue: UIStoryboardSegue, sender: Any?) {
       // You can't guaranty order here, so some duplication required
       if let controller = segue as? MapController {
           mapController = controller
           playlistController?.songSelectable = controller

       }
       else if let controller = segue as? PlaylistController {
           playlistController = controller 
           controller.songSelectable = mapController
       }
   }
}

Another option: establish connection via blocks. It is more modern and Swifty way:

typealias SongHandler = (Song)->()

class PlaylistController: UIViewController {
    var songHandler = SongHandler?
}

class MapController: UIViewController {
    func songSelected(_ song: Song)  {
        //do your code
    }
}

class MainController: UIViewController {
   weak var mapController: MapController?

   func prepare(for segue: UIStoryboardSegue, sender: Any?) {
       if let controller = segue as? MapController {
           mapController = controller
       }
       else if let controller = segue as? PlaylistController {
           controller.songHandler = { [unowned self] (song) in self.mapController?.songSelected(song)
       }
   }
}

Mind that code fragments above is just illustrations, you have plenty of ways to access controllers, setup delegation or blocks.

MichaelV
  • 1,231
  • 8
  • 10
  • Thank you very much. I'm pretty new to swift so if I can get small explanation to see if I understand you right. The prepre for segue is waiting for an action ? Like tap button ? Who is the "sender" ? Or it basicly function that work without a sender and then I can just return from the function a songlist ? – Tubul Jan 12 '18 at 16:58
  • It's relevant if you are using storyboards to layout your UI. When you placing your scenes in storyboard, you are connecting them with segues. There is a specific Container relationship, that allow you to present several ViewControllers on scene. Before segue executed, it had to prepare itself, so it called `prepare()` function. In this particular case it will be executed after viewDidLoad, but before viewWillAppear, and sender will be Main view controller itself. If you placing your VC without storyboards, it's even simpler - you had to instantiate them manually. – MichaelV Jan 12 '18 at 17:18
  • Ok great man really appriciate the patience. I still think I missing something. With your suggestion/example do I need to preformsegue ? Or does the connection segue between them is enough and the sender is loading while viewdidload. Im asking this because google maps is different view and Im using function that give me markers in 300m distance after I prefom the func I want to pass this details to playlistVC that is a stand alone view controller. There is not any connection between those views. So after preforming this function how can I load the songlist that I created in mapVC to playlistVC – Tubul Jan 12 '18 at 21:11
  • Just to be clear with the "sender" I mean when the prepre function is called without doing preform segue ? While moving to the other view ? – Tubul Jan 12 '18 at 21:13