Using Swift 3.0, could I pass data to a view controller that is not segued? - swift

I have 3 view controllers in my program. Can I send more than 1 value from the first view controller to the third without segue-ing?
Or do I have to send the values to the first, the second, then the third?
Thanks in advance.

You shouldn't rely on passing values directly like you are describing because you don't even know if all of the controllers have been instantiated yet. Create a model class or struct that you can store values in and reference from all 3 controllers. Basic example of model class:
import Foundation
fileprivate let sharedModelInstance = Model()
class Model {
var basicString : String = "string"
static var sharedInstance : Model {
return sharedModelInstance
}
func setBasicString(_ str: String) {
basicString = string
}
}
Then a controller:
import UIKit
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// reads 'string'
let aStr : String = Model.sharedInstance.basicString
Model.sharedInstance.setBasicString("Hello")
// Now 'Hello'
let anotherStr : String = Model.sharedInstance.basicString
}
}
This way all of your data is centralized and as long as you always refer back to your model you will never have data between controllers that is out of sync.

There are many ways to pass data from one view controller to another. What they all have in common is that the views are already created when the data is being passed. In your scenario, they have not all been created.
So if you're going from 1 to 2 to 3, when 1 is showing, 3 hasn't been instantiated yet. You would have to create 3, set the variable, then pass that newly created view controller 3 to view controller 2 so that 2 could then go ahead and display the already-created 3 on the screen. That whole process defeats the main purpose of your question and is much more work than the steps you're trying to avoid.
If you have one or two variables to pass, it's not such a hassle to pass to 2 then to 3. If you have a lot of data, you may want to consider putting it into one array to pass, saving it to Core Data, or even changing your data model completely.

Related

storing temporary data from user through different controllers before saving them

Best practice for storing temporary data during session? I have an app that allows me to register a customer in a medical service, inserting biometrical and personal data (name, weight, personal measures etch.) once the data are saved, you go to other pages, where you can perform some operation using the data. Only at the very end of this path (after 3-4 controllers), I save the Customer in local database via Realm.
during register phase, I need to store this "TempUser"'s data, with can be accessed from multiple controllers.
I think I could use singletons, but not sure IF and HOW
I fund this but not sure which solution fits better.
my idea:
class AppSession {
static var shared = AppSession()
private init() {}
var currentPatient: Patient?
}
class Patient {
let shared = Patient()
private init() {}
var name: String? = ""
var weight: Double = 0.0
}
and in my textfield delegate:
AppSession.shared.currentPatient?.name = data //text from textfield
otherwise, using Patient as a struct, not a class
It is hard to tell what solution fits for every individual case. It really depends how broadly the data is used in your app. Is it common to every part or just some specific ViewControllers ? How many times it is retrieved and updated ? Also it depends on the architecture pattern you are using and many more factors. So in short, your solution using a singleton pattern may be valid, but can't tell for sure without having a deep look at your project. ​If you decide to stick with your solution, you can omit the AppSession class and directly access your Patient.
class Patient {
static let shared = Patient()
var name: String? = ""
var weight: Double = 0.0
}
And update it:
Patient.shared.name = ""
Even though your solution using a singleton pattern may be valid as mentioned above, you should also consider the user point of view: Do you want the user to be able to continue the flow even after the app was killed ? If yes, than you should save the data after every step(So maybe to KeyChain as what you describe is kind of sensitive data). Or a better solution is to store the data on the server side, which gives the user the flexibility to continue even on an other device(maybe android).
But you can say, that you really do not care about KeyChain neither communicating with a server, and want to do it locally. In this case personally I would pass the data between the controllers, and avoid Singletons. Let's take your Patient object and create a protocol which will require every UIViewController that conforms to it to have a stored property of Patient.
struct Patient {
var name: String? = ""
var weight: Double = 0.0
}
protocol MyOnboardingProtocol: class {
var data: Patient { get set }
}
With the MyOnboardingProtocol you will mark every controller which will require to hold the Patient data. In this way you will force the ViewControllers to hold the Patient object, and will know for the first glance if your ViewController belongs to the "Onboarding" flow.
In your first UIViewController, where your flow begins, you will initialise your Patient object(maybe also make some update on it from user input), and pass along for the next UIViewController:
class MyFirstViewController: UIViewController, MyOnboardingProtocol {
var data: Patient = Patient()
func nextViewController() {
let viewController = MySecondViewController(data: data)
navigationController?.pushViewController(viewController, animated: true)
}
}
For every other UIViewController down the road you will pass the updated object into the init:
class MySecondViewController: UIViewController, MyOnboardingProtocol {
var data: Patient
init(data: Patient) {
self.data = data
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
But as I mentioned before, what I described is just one way of doing it. It will really depend on your individual case and business logic. As I want to keep my answer short, I encourage you to discover the topic a bit more, there has been many great articles about this topic.

Memory access conflict with structs and observer pattern

I'm implementing an Observer design pattern on a Struct model object. The idea is that I will pass my model down a chain of UIViewController and as each controller modifies it, previous controllers will also be updated with changes to the object.
I'm aware this problem could be solved by using a class instead of struct and modifying the object directly through reference, however I'm trying to learn more about using structs.
struct ModelObject {
var data: Int = 0 {
didSet {
self.notify()
}
}
private var observers = [ModelObserver]()
mutating func attachObserver(_ observer: ModelObserver){
self.observers.append(observer)
}
private func notify(){
for observer in observers {
observer.modelUpdated(self)
}
}
}
protocol ModelObserver {
var observerID: Int { get }
func modelUpdated(_ model: ModelObject)
}
class MyViewController : UIViewController, ModelObserver {
var observerID: Int = 1
var model = ModelObject()
override func viewDidLoad() {
self.model.attachObserver(self)
self.model.data = 777
}
func modelUpdated(_ model: ModelObject) {
print("received updated model")
self.model = model //<-- problem code
}
}
Simply put, my model object notifies any observer when data changes by calling notify().
My problem right now is memory access: when data gets set to 777, self.model becomes exclusively accessed, and when it calls notify which calls modelUpdated and eventually self.model = model, we get an error:
Simultaneous accesses to 0x7fd8ee401168, but modification requires exclusive access.
How can I solve this memory access issue?
If you're observing "a thing," then that "thing" has an identity. It's a particular thing that you're observing. You can't observe the number 4. It's a value; it has no identity. Every 4 is the same as every other 4. Structs are values. They have no identity. You should not try to observe them any more than you'd try to observe an Int (Int is in fact a struct in Swift).
Every time you pass a struct to a function, a copy of that struct is made. So when you say self.model = model, you're saying "make a copy of model, and assign it to this property." But you're still in an exclusive access block because every time you modify a struct, that also makes a copy.
If you mean to observe ModelObject, then ModelObject should be a reference type, a class. Then you can talk about "this particular ModelObject" rather than "a ModelObject that contains these values, and is indistinguishable from any other ModelObject which contains the same values."

How to keep two properties in sync using bind(_:to:withKeyPath:options:)?

I want to keep two properties in sync with Cocoa bindings.
In my code, you can see that I have two classes: A and B. I wish to keep the message values in A and B instances synchronized so that a change in one is reflected in the other. I'm trying to use the bind(_:to:withKeyPath:options:) method of the NSKeyValueBindingCreation informal protocol. I use Swift 4.2 on macOS.
import Cocoa
class A: NSObject {
#objc dynamic var message = ""
}
class B: NSObject {
#objc dynamic var message = ""
init(_ a: A) {
super.init()
self.bind(#keyPath(message), to: a, withKeyPath: \.message, options: nil) // compile error
}
}
I get a compile error in the line where I call bind: cannot convert value of type 'String' to expected argument type 'NSBindingName'. I get the suggestion to wrap the first parameter with NSBindingName(rawValue: ). After applying that, I get the error type of expression is ambiguous without more context for the third parameter.
What am I doing wrong?
I made the following example in a playground. Instead of class A and B, I used a Counter class since it is more descriptive and easier to understand.
import Cocoa
class Counter: NSObject {
// Number we want to bind
#objc dynamic var number: Int
override init() {
number = 0
super.init()
}
}
// Create two counters and bind the number of the second counter to the number of the first counter
let firstCounter = Counter()
let secondCounter = Counter()
// You can do this in the constructor. This is for illustration purposes.
firstCounter.bind(NSBindingName(rawValue: #keyPath(Counter.number)), to: secondCounter, withKeyPath: #keyPath(Counter.number), options: nil)
secondCounter.bind(NSBindingName(rawValue: #keyPath(Counter.number)), to: firstCounter, withKeyPath: #keyPath(Counter.number), options: nil)
secondCounter.number = 10
firstCounter.number // Outputs 10
secondCounter.number // Outputs 10
firstCounter.number = 60
firstCounter.number // Outputs 60
secondCounter.number // Outputs 60
Normally bindings are used to bind values between your interface and a controller, or between controller objects, or between controller object and your model objects. They are designed to remove glue code between your interface and your data model.
If you only want to keep values between your own objects in sync, I suggest you use Key-Value Observing instead. It has more benefits and it is easier. While NSView and NSViewController manages bindings for you, you must unbind your own objects, before they are deallocated, because the binding object keeps a weak reference to the other object. This is handled more gracefully with KVO.
Take a look at WWDC2017 Session 212 - What's New in Foundation. It shows how to use key paths and KVO in a modern application.

Patterns: Singletons vs. Static vars and methods approach

I am reading a lot about the Singleton Pattern. I am currently using it to store groups of global state in my first app. I am reaching a point where I wonder which approach to implement API client classes and similar with.
Are Structs with static vars and static functions having the same issues?
To illustrate what I mean, I've tried to write the same heavily simplified and exact same(?) scenario twice.
1. A singleton being worked with by a view controller:
struct APIClientSingletonClass {
static let shared = APIClientSingletonClass()
var stateOfSomehting: Bool = true
var stateOfSomehtingMore: Bool = false
var stateNumber: CGFloat = 1234
var stateOfSomehtingComputed: CGFloat {
return stateNumber * 10
}
func convertSomethingToSomethingElse() {
// calling method in self like this:
otherMethod()
}
func otherMethod() {
// doing stuff here
}
}
// Calling APIClient from outside:
class ViewControllerTalkingToSingleton: UIViewController {
var api = APIClientSingletonClass.shared
override func viewDidLoad() {
super.viewDidLoad()
api.convertSomethingToSomethingElse()
api.stateOfSomehting = false
}
}
2. Another approach:
struct APIClientStruct {
static var stateOfSomehting: Bool = true
static var stateOfSomehtingMore: Bool = false
static var stateNumber: CGFloat = 1234
static var stateOfSomehtingComputed: CGFloat {
return stateNumber * 10
}
static func convertSomethingToSomethingElse() {
// calling method in self like this:
APIClientStruct.otherMethod()
}
static func otherMethod() {
// doing stuff here
}
}
// Calling APIClient from outside:
class ViewControllerTalkingToStruct: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIClientStruct.convertSomethingToSomethingElse()
APIClientStruct.stateOfSomehting = false
}
}
What do you guys think? Is approach 2 falling into the same traps that seem to make Singletons such a double-edged sword?
Any input is really appreciated!
Best from Berlin
EDIT:
This thread is pretty interesting, but I'm not sure it really relates to my question:
Difference between static class and singleton pattern?
Since there are many perspectives on this topic, let me specify:
Does my approach 2 have the same problem implications with testing and code maintainability?
A class-based singleton is the way to go, provided you accommodate for dependency injection for your tests. The way to do this is to create a single singleton for your app, called, say, DependencyManager. In your AppDelegate (or from other classes if needed), you'd create whatever controllers, network services, realm models, etc you want to hang on your DependencyManager, and then assign them to the DependencyManager. This code would be skipped by your unit tests.
Your unit tests can then access the DependencyManager (and thus instantiate the DependencyManager during first access), and populate it with mock versions of those controllers and services to whatever degree each unit test desires.
Your UIViewControllers, your MVVM view models, etc... can access the DependencyManager as a singleton, and thus get either the real controllers and services, or a mock version of them, depending on if you're running the app or unit tests.
If you're doing MVVM, I also recommend that when a UIViewController is about to create its view model class, that it first checks a special property in the DependencyManager to see if a mockViewModel exists. A single property can serve this purpose, as only one of your UIViewControllers ever would be tested at once. It'd use that property instead of creating a new view model for itself. In this way, you can mock your view models when testing each UIViewController. (There's other tricks involved to being able to prop up a single UIViewController for testing, but I won't cover that here).
Note that all of the above can work very nicely with an app that also wants to use storyboards and/or nibs. People are so down on storyboards because they can't figure out how to do dependency injection of mock services for their view controllers. Well, the above is the solution! Just make sure in your AppDelegate to load the storyboard AFTER setting up the DependencyManager. (Remove the storyboard name from your info.plist, and instantiate it yourself in AppDelegate).
I've written a few shipped apps this way, as well as some sample apps for an SDK, along with the tests. I highly recommend the approach! And be sure to write your unit tests and viewController tests either during or at least immediately after development of each such class, or you'll never get around to them!
What generally makes sinlgetons hard to test is that the singleton objects are typically always accessed directly . Because of this, you don't have a means to substitute the real singleton object (e.g. a data-store that's backed by a database) with a mock object for testing (e.g. a data-store that's backed by an easily-configurable array of predefined test data).
Using static members has the same fundamental issue. When referencing a static member directly, you don't have a means of substituting a mock object in place of the real prod implementation.
The solution to this is quite simple: don't access singleton members directly. What I do is something like this:
// An example of a dependency.
protocol DataAccessLayer {
func getData() -> [Int]
}
// A real implementation of DataAccessLayer, backed by a real production database
class ProdDB: DataAccessLayer {
static let instance = ProdDB()
private init() {}
func getData() -> [Int] {
return [1, 2, 3] // pretend this actually queries a DB
}
}
// A mcok implementation of DataAccessLayer, made for simple testing using mock data, without involving a production database.
class MockDB: DataAccessLayer {
func getData() -> [Int] {
return [1, 2, 3] // The mock *actually* hardcodes this data
}
}
// A protocol that stores all databases and services used throughout your app
protocol ServiceContextProtocol {
var dataAccessLayer: DataAccessLayer { get } // Present via protocol, either real impl or mock can go here
//var fooAPIGateway: FooAPIGateway { get }
//... add all other databases and services here
}
// The real service context, containing real databases and service gateways
class ProdServiceContext: ServiceContextProtocol {
let dataAccessLayer: DataAccessLayer = ProdDB.instance
//var fooAPIGateway: ProdFooAPIGateway { get }
//... add all other prod databases and services here
}
// A mock service context, used in testing, which provides mocked databases and service gatways
class MockServiceContext: ServiceContextProtocol {
let dataAccessLayer: DataAccessLayer = MockDB()
//var fooAPIGateway: MockFooAPIGateway { get }
//... add all other mock databases and services here
}
let debug = false // Set this true when you're running in a test context
// A global variable through which you access all other global state (databases, services, etc.)
let ServiceContext: ServiceContextProtocol = debug ? MockServiceContext() : ProdServiceContext()
// Always reference ServiceContext.dataAccessLayer, ServiceContext.fooAPIGateway, etc.
// and *never* reference ProdDB.instance of MockDB directly.
I would use a Class based Singleton. Just remember the 2 criteria for having a singleton. You want GLOBAL ACCESS and SINGLE INSTANCE in your program. There is a couple problems where struct based singleton would fail. Once you assign a struct to a new variable, Swift makes a complete copy under the hood.
Another useful snip of information can be found using this link.
What's the difference between Struct based and Class based singletons?

Is it possible to assign an array to a class property by reference rather than a copy?

Background:
I designed a TableViewDataSource class that provides an implementation for UITableViewDataSource and UITableViewDelegate. You instantiate TableViewSection objects, which are passed to the TableViewDataSource which are used to configure cells, section headers, handle selection, row insertion, etc.
The TableViewSection object has a property called dataSource: [AnyObject]?, which, when set, is used to calculate the number of rows in the section, and provide an object for the cell configuration block:
// get the section, dequeue a cell for that section, retrieve the item from the dataSource
// ...
tableSection.cellConfigurationBlock?(cell: AnyObject, item: AnyObject?, indexPath: NSIndexPath)
return cell
What I'd like to do is assign a reference to an array from my viewModel to my tableSection.dataSource, having my viewModel update the array, in turn updating the table view. In Swift, you cannot pass an array by reference. The workaround seems to be to use an NSMutableArray, but with that comes a loss of type safety, and greater cognitive load while translating objects back and forth from Swift to Foundation.
Working Example:
let kCellIdentifier = "SomeCellIdentifier"
class MyViewController: UITableViewController {
// Property declarations
#IBOutlet var tableDataSource: TableViewDataSource!
var viewModel: MyViewControllerViewModel = MyViewControllerViewModel()
override func viewDidLoad() {
super.viewDidLoad()
self.setupTableView()
self.refresh()
}
func setupTableView() {
var tableSection = TableViewSection(cellIdentifier: kCellIdentifier)
tableSection.dataSource = self.viewModel.collection
// tableSection configuration
// ...
self.tableDataSource.addSection(tableSection)
}
func refresh() {
self.viewModel
.refresh()
.subscribeNext({ result in
self.tableView.reloadData()
}, error: { error in
self.logger.error(error.localizedDescription)
})
}
}
The refresh() method on the viewModel hits my API service, updates it's collection property on response, and provides the result on the next event of an RACSignal (RACSignal is a class provided by Reactive Cocoa and really, besides the point).
I've found one workaround, which involves reassigning the data source each time a single update is made, or after a batch update.
func refresh() {
self.viewModel
.refresh()
.subscribeNext({ result in
self.updateDataSource()
self.tableView.reloadData()
}, error: { error in
self.logger.error(error.localizedDescription)
})
}
func updateDataSource() {
self.tableDataSource.tableSectionForIndex(0)?.dataSource = viewModel.collection
}
This approach works, but only temporarily as a workaround. As a TableViewDataSource grows and becomes more complex, this method becomes increasingly more complex with imperative, procedural code, the opposite of what I set out to achieve when writing the class.
Question
Is there any workaround to stick to native Swift Array's to achieve the equivalent of passing a Foundation NSArray or NSMutableArray by reference?
Bonus Question
Can someone provide me with some class/struct design tips to accomplish the desired goal in pure Swift?
The simple solution is to wrap the array in a class. The class instance is passed by reference so the problem is effectively solved: a change to the array through any reference to the class instance affects the array as seen through every reference to that class instance.
The class in question can be extremely lightweight - basically, it just serves as a thin wrapper that carries the array along with it, and a client accesses the array directly through the class instance - or, just the opposite, you can design the class to manage the array, i.e. the class deliberately presents an array-like API that shields clients from the underlying implementation. Either approach might be appropriate; I've certainly done both.
Here's an example of the first kind of situation. My model object is an array belonging to a UIDocument subclass. My view controller is a UITableViewController. The user is going to view, add, and edit model entities in the table. Thus, the UITableViewController needs access to the UIDocument's array (which happens to be called people).
In Objective-C, my UITableViewController simply held a reference to the array, self.people, which was an NSMutableArray. This was just a pointer, so changes to self.people were also changes to the UIDocument's people - they are one and the same object.
In Swift, my UITableViewController holds a reference to the UIDocument object, self.doc. The array, which is now a Swift array, is "inside" it, so I can refer to it as self.doc.people. However, that's too much rewriting! Instead, I've created a calculated variable property self.people which acts as a gateway to self.doc.people:
var doc : PeopleDocument!
var people : [Person] { // front end for the document's model object
get {
return self.doc.people
}
set (val) {
self.doc.people = val
}
}
Hey presto, problem solved. Whenever I say something like self.people.append(newPerson), I'm passed right through to the UIDocument's model object people and I'm actually appending to that. The code thus looks and works just like it did in Objective-C, with no fuss at all.