class instance to all ViewControllers - ios

I wish to avoid writing let APIHelper = API() in every UIViewController, instead I did this:
extension UIViewController {
func APIHelper() -> API {
let api = API()
return api
}
}
and now it is working like self.APIHelper().callMethod(), but I'm not really sure if it is the way to do it. Any tips on best practice?

Your extension useless, since it just same as calling API() everytime:
self.APIHelper().callMethod()
self.APIHelper().callSecondMethod() //here you created another API instance
same as
API().callMethod()
API().callSecondMethod()
If API is singletone, idea looks ok, but in swift you usually create singletone with static constant:
class API {
static let sharedAPI = API()
//...
}
and access to it like this:
API.sharedAPI.callMethod()
API.sharedAPI.callSecondMethod() //now called in same API instance
If you don't want to write API.sharedAPI everytime, then you can use:
Your extension
extension UIViewController {
var apiHelper: API {return API.sharedAPI}
}
Not recommended as #NickCatib explained.
Base view controller
as #NickCatib suggested (easier with variable):
class BaseViewController: UIViewController {
// some of the code you might need
let apiHelper = API.sharedAPI
}
Protocol
If you use API in view controllers time to time, can be better declare protocol
protocol APIHelper {
var apiHelper: API {get}
}
with default implementation
extension APIHelper {
var apiHelper: API {return API.sharedAPI}
}
and connect it to your viewController only when needed
class ViewControllerThatNeedsAPI: UIViewController, APIHelper {
// apiHelper avalible here
}
With all three ways you access your API like this:
apiHelper.callMethod()

How about some kind of base view controller where you can extend with that declaration? This way ALL of your view controllers have that function, even when you don't need it.
This would go like
class BaseViewController: UIViewController {
// some of the code you might need
func APIHelper() -> API {
let api = API()
return api
}
}
And later:
class ViewControllerThatNeedsAPI : BaseViewController {
// You have it here
}
Another approach that I actually use is to have service/manager for API calls that handles that, and send all the data needed via delegate/NSNotification/completion handler. That way your code will be cleaner and easier to test ( if you practice tests ). If you keep everything in the view controller you will break the SRP. This managers are PONSO - Plain old ns objects. You could use the same way as for view controllers and have some BaseService with API URL, basic stuff that needs to be overriden etc. After that you just implement service and call it when needed - depending on the implementation have some function to reflect data to UI.

Related

Proper way to pass multiple values with protocols in iOS

So I have two ViewControllers. First (MapVC) with map and second (SettingsVC) with many settings that need to be applied to this map.
I thought it would be nice idea to create protocol like
protocol MapSettingsDelegate: class {}
I know that I can specify function inside this protocol. But how I should do it when I have many settings - how should I pass them from SettingsVC to MapVC.
Example:
struct MySettings {
var value1: String
var value2: String
// and so on...
}
protocol MapSettingsDelegate: class {
func settingsUpdated(newSettings: MySettings)
}
and implement it inside your controller
class MapVC : MapSettingsDelegate {
...
func settingsUpdated(newSettings: MySettings) {
// Update everything you need
}
...
}
Feel free to ask for details

How to get references for ViewController

Community!
I have a question regarding References in Swift 3 and Xcode.
My Plan is this:
At the moment my App has 4 Controllers. A MapViewController, a PositionController, a MarkerController and a APIDataController. Those of course handle the respective Models. My problem now comes in, when I want the controllers to communicate. Look at this code (Please ignore that most functions aren't implemented here):
First my MarkerController:
import Foundation
protocol MarkerControllerDelegate: class{
func willGetMarkerArray()
func didGetMarkerArray(_ newMarkerArray: Marker)
}
class MarkerController: NSObject{
var markerArray = [Marker]()
weak var delegate: MarkerControllerDelegate?
func createMarkerArray(){
}
func getMarkerArray() -> [Marker]{
return markerArray
}
}
extension MarkerController : APIDataControllerDelegate{
func didRetriveAPIData(_ APIDataModelArray: APIDataModel) {
createMarkerArray()
}
}
And now my APIDataController:
import Foundation
protocol APIDataControllerDelegate: class{
func didRetriveAPIData(_ APIDataModelArray: [APIDataModel])
}
class APIDataController: NSObject {
var APIDataModelArray = [APIDataModel]()
weak var delegate: APIDataControllerDelegate?
func retriveAPIData(){
//get the data
//.....
//finished getting data
delegate?.didRetriveAPIData(APIDataModelArray)
}
func getAPIDataModelArray() -> [APIDataModel] {
return APIDataModelArray
}
}
And finally the beginning of my MapViewController:
import UIKit
import GoogleMaps
import GoogleMaps
import CoreLocation
class MapViewController: UIViewController{
let positionController = PositionController()
let apiDataController = APIDataController()
let markerController = MarkerController()
override func viewDidLoad() {
super.viewDidLoad()
apiDataController.retriveAPIData()
//blablabla
}
//blablablabla
}
Now, this might be a lot of code at once, so let me explain you what is ought to happen:
At the beginning (or wherever in the code) MapViewController calls the retrieveAPIData method. Once the function is finished, APIDataController is planned to notify the MarkerController via the APIDataControllerDelegate-Protocol to start his work (run his createMarkerArray-Method).
Now, to do this, I have to declare a markerController-Instance as a Delegate to APIDataController. Now, since I have already created instances of all my controllers in mapViewController, I want to give those references to my other controllers where needed so that they all refer to the same Instances, kinda like a Singleton.
But, how do I create a Reference to my MapViewController IN my MapViewController, to pass it to my other Controllers so that they can from there go like:
let positionController = mapViewController.positionController?
Nothing I tried seem to work. So any help would be appreciated.
Also if there are better ways to let my Controllers communicate I am open to here them, I am sure there is a better way to do it than I currently try to do.
Using Delegates for the communication between the controllers is the recommended way to do it. Setting the delegates depends on the way these controllers interact in your app. Often you will have a segue from one app to another, that is a good chance to set delegates. You can also do it in code that is putting together your views

Swift: Store Type of implementation of Protocol in static var of protocol extension

Okay so I have this protocol MenuEntry which I want to use to populate a TableView:
protocol MenuEntry {
static var title: String { get }
static func entrySelected(_ menuController: MenuController)
}
I want to implement this protocol in various places and let the item itself decide what to do. It might be a UIViewController which implements the protocol or a simple struct, which then calls a function on the menu itself:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
Now I want to build the MenuControllers datasource but without actually instantiating the entries because especially my view controllers are not necessarily available when the MenuControllers datasource is populated. Thats why I use static var/func in MenuEntry. Currently, I can simply do this to populate the datasource:
let dataSource: [MenuEntry.Type] = [SomeEntry.self]
And it seems to work pretty well. I can get the entries and call the corresponding functions:
dataSource.first?.title //Gives me "Some Entry"
Now comes the tricky part. I thought I could be really clever and create a protocol extension where I reference all the types in which I implement the protocol like so:
extension MenuEntry {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
//...
}
And then later use them via MenuEntry.someEntry. However, accessing someEntry on MenuEntry gives me an error:
error: static member 'someEntry' cannot be used on protocol metatype 'MenuEntry.Protocol'
So my question is: what am I missing? Am I just trying to misuse the language in a way which is not intended or am I just doing something wrong?
SOLUTION
From the accepted answer below, heres how I now do things. First, we need the mentioned struct (no need for a class I guess):
struct MenuEntries {}
Then, where ever I implement the MenuEntry protocol, I also extend this struct and add the entry like so:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
extension MenuEntries {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
}
The last thing is to create my datasource like so:
let dataSource: [MenuEntry.Type] = [MenuEntries.someEntry, ...]
Okay, now I have a list of all menu entries in one place. The downside is I have to remember to extend MenuEntries every time. Except there is some magical way to extend a struct on a conditional base I'm not aware of. But I guess thats just over the top and simply not possible.
From The Swift Book
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.”
Your extension is attempting to implement functionality directly in the protocol, but this is not allowed; Only a class, structure or enumeration adopting the protocol can provide functionality.
You could define a class that returns your menu classes:
class MenuFactory {
static var someEntry: MenuEntry.type { return SomeEntry.self }
}

Protocol Oriented Programming, implicitly calling extension method

Having my first crack at POP. In this case I want to decorate some UIViewControllers so that any that they automatically raise a 'Page viewed' analytics event.
So I created a protocol, and and extension for that protocol:
protocol ReportPageViewedEvent {
func reportPageViewed()
var pageName : String? { get set }
}
extension ReportPageViewedEvent where Self: UIViewController
{
func reportPageViewed()
{
guard let pageName = self.pageName else
{
fatalError("UIViewController implements ReportPageViewEvent protocol but did not set pageName property")
}
let eventBusiness = EventBusiness.sharedInstance
eventBusiness.logUserViewedPage(pageName)
}
}
This works as I want, if I decorate a UIViewController with ReportPageViewedEvent like this:
class HomeView: UIViewController, ReportPageViewedEvent {
I get a compiler error unless I set 'pageName' which is exactly what I want.
Where I am getting unstuck is where and how to call the actual reportPageViewed() method. I really want it to be called from viewDidLoad which means I either have to modify every 'viewDidLoad' in every controller that uses it, or subclass and call the method in the super class which defies the point of using POP in the first place.
Is there a nice way to achieve this. I can't find an example like this in any tutorial/blog.
Basically, there is always some behaviour shared by all the screens of your app. So it is appropriate to create a class called (for example) BaseViewController so all the other view controllers will inherit from it.
In BaseViewController's viewDidLoad you can call the reportPageViewed() method.
However, this approach makes the Protocol Oriented Programming not needed. Protocols are useful when you need to assign some same behaviour to objects that have nothing in common (which is not the case for app screens).

Non UIViewController Class in Swift

I'm new to iOS. I want to know how to create a non UIViewController based class. I mean to say the class not extending from UIViewcontroller.
I want so because I want to call some Async HTTP url and parse the Json/XML in non UI class. I just want to do them in a separate class from UIViewcontroller classes.
Create a new class without inheritance like this:
class JSONObject{
var url: NSURL?
internal func parseJSON() -> NSArray {
// Code to parse your json
return []
}
}
and then you can create an instance of the class elsewhere using something like this:
var newJSONObject: JSONObject = JSONObject()
newJSONObject.url = NSURL(string: "www.somejson.php")
newJSONObject.parseJSON()

Resources