Implement Alamofire into DDD structure - swift

Qustion
I have a following structure
class UITableViewController (Presentation) -> class Contents(Domain) -> class API(Infrastructure)
Contents class gets raw data via API class and forms contents and then passes to UITableViewController.
I would like to use Alamofire to do Networking in API class.
I’ve looked through stackoverflow and I only found examples that UITableViewController directly accesses API class. Direct access from Presentation layer to Infrastructure layer is something we should not do.
Like this
How to return value from Alamofire
How do I achieve implementing Alamofire into DDD structure?
I want to achieve something like this
UITableViewController
class MyTableViewController: UITableViewController {
var contents: Contents?
override func viewDidLoad() {
super.viewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
self.contents = Contents.get("MyContents")
self.tableView.reloadData()
}
}
}
Contents
class Contents: NSObject {
static func get(contentsName: String) -> Contents {
let data = MyAPI.getRequest("https://xxxx.com/myContents")
// Form contents
let contents = ContentsFactory(data)
return contents
}
}
API
class MyAPI: NSObject {
static getRequest(url) -> NSData {
// Get and return data using alamofire
}
}

You need to deal with the get request being run in the background, whereas the instance of Contents will be returned immediately. In the completion handler for the Alamofire request, you will need it to call back to the Contents instance, which will then call back to the ViewController.
Therefore your Contents class needs to have a completion closure passed on the get function, along the lines of:
static func get(contentsName: String, completion: (() -> Void)?) -> Contents {
This closure will have to be passed down to the API (or through other techniques), so it can ultimately be called when the Alamofire request completes. Also, rather than have the class method do all the background work, it would be better handled in the Contents instance you create.
Then you will call using:
self.contents = Contents.get("MyContents", completion({
dispatch_async(dispatch_get_global_queue(priority, 0)) {
self.tableView.reloadData()
}
}

Related

How to create separate data mapping file with function names in Swift

I have a macOS app that I'm creating in Swift and I have integrated an external HID device that has a number of controls on it.
The HID part is done where I am receiving all of the hid commands from the device and I am trying to create a mapping file where I can maintain the HID key mappings in a separate swift file.
All I want in that file is the data and what I want to do is this;
raw hid data is received from HID device (In ViewController)
Lookup the function name assigned to this hid data (In separate file)
Run the function that is mapped to that key. (Function located in the main ViewController)
So far I have the external swift file setup with all of the mapping and that all works fine but my issue is when I try to call the looked up function in the ViewController, it says the function can't be found in the scope.
Initially I thought I would use a delegate but the external file isn't a viewcontroller, just a separate swift file so I don't know if I can do that?.
I've tried searching but everything I've found is calling a function from another ViewController which I'm not. It's very possible I'm not using the best approach and my goal is to just keep all of the mapping in a separate file as there is a lot and it woudl be easier to maintain.
Any suggestions are appreciated.
This is one way to achieve this. It can get tedious. You can totally skip writing out a separate protocol for the delegate, but this is cleaner design.
protocol HIDMessageDelegate: AnyObject {
// example messages
func message1()
func message2()
func message3()
}
class HIDMessageParser {
static weak var delegate: HIDMessageDelegate?
static func parseHIDMessage() {
var condition = 0
// this is where your switch statement will go and you'll parse things and call the relevant delegate method
switch (condition) {
default:
delegate?.message1()
}
}
}
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
HIDMessageParser.delegate = self
}
}
extension MyViewController: HIDMessageDelegate {
func message1() {
}
func message2() {
}
func message3() {
}
}
You can simply create a UIViewController as the external file and add it as a property to the main ViewController.
In the external file add this.
#IBOutlet var uiViewController: UIViewController!
In the ViewController add this.
var externalFileViewController: UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
externalFileViewController = externalFileViewController?.loadView()
// If we have an object then load it
if let viewController = externalFileViewController {
viewController.view.frame = view.frame
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(viewController.view)
uiViewController = viewController
}
}
Now in the viewController look up the functions to be called from the external file and call them using the function name.
The functions are defined in the external file using #IBAction.
Let me know if you have any questions.

How can I send fetched data to my ViewController?

Heres a caption of my API call:
So, I've got the abilities of the pokemons I needed, but now, idk how to get that data out of my Service class (where I'm doing all the parsing), and send it to my InfoViewController.
My purpose is to fetch that data on some label, and then show the ability names for every poke, according to their ID. Here is a caption of my app:
I wanna add an "Ability" label below Weight, and that's where I wanna assign the data. I have a whole CollectionView with all the pokemons, and the goal is assign the correct ability for each one of them.
I'm kinda struggling for a practical (and less verbose) way to reach this.
I apreciated every comment, any advice and suggestion too. Thanks!
EDIT: Heres my code:
extension InfoController: ServiceDelegate {
func finishedWithPokemonAbilities(abilities: [String], id: Int) {
self.abilities = abilities
self.ids = id
print(abilities)
}
}
You can create a custom Protocol(could call it PokemonServiceDelegate as an example) that your InfoViewController would inherit and implement. On your service object(I'm using PokemonService in the example) create a property with a type of PokemonServiceDelegate and set that property to the view controller that you want to receive the data. After the service finishes fetching the data, update the delegate by passing the data in the function declared in the protocol.
// Protocol your view controller will inherit
protocol PokemonServiceDelegate {
// Function your view controller will implement
func finishedWithPokemonAbilities(abilities: [String])
}
class InfoViewController: UIViewController {
// Reference to the service that makes the request
var service: PokemonService
override func viewDidLoad() {
...
// Set the delegate of the service to self
service.delegate = self
...
}
}
extension InfoViewController: PokemonServiceDelegate {
// Implement the protocol
func finishedWithPokemonAbilities(abilities: [String]) {
// Do something with their abilities here
}
}
struct PokemonService {
var delegate: PokemonServiceDelegate?
// The function that you call to get your abilities
func someUpdateFunc() {
...
let abilities = json[abilities].arrayValue.map {$0["ability"]["name"].stringValue}
delegate?.finishedWithPokemonAbilities(abilities: abilities)
...
}
}

Way to check data modification across many viewcontroller without using global variables?

I have an app which contains several viewControllers. On the viewDidAppear() of the first VC I call a set of functions which populate some arrays with information pulled from a database and then reload table data for a tableView. The functions all work perfectly fine and the desired result is achieved every time. What I am concerned about is how often viewDidAppear() is called. I do not think (unless I am wrong) it is a good idea for the refreshing functions to be automatically called and reload all of the data every time the view appears. I cannot put it into the viewDidLoad() because the tableView is part of a tab bar and if there are some modifications done to the data in any of the other tabs, the viewDidLoad() will not be called when tabbing back over and it would need to reload at this point (as modifications were made). I thought to use a set of variables to check if any modifications were done to the data from any of the other viewControllers to then conditionally tell the VDA to run or not. Generally:
override func viewDidAppear(_ animated: Bool) {
if condition {
//run functions
} else{
//don't run functions
}
}
The issue with this is that the data can be modified from many different viewControllers which may not segue back to the one of interest for the viewDidAppear() (so using a prepareForSegue wouldn't work necessarily). What is the best way to 'check' if the data has been modified. Again, I figured a set of bool variables would work well, but I want to stay away from using too many global variables. Any ideas?
Notification Center
struct NotificationName {
static let MyNotificationName = "kMyNotificationName"
}
class First {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(self.notificationReceived), name: NotificationName.MyNotificationName, object: nil)
}
func notificationReceived() {
// Refresh table view here
}
}
class Second {
func postNotification() {
NotificationCenter.default.post(name: NotificationName.MyNotificationName, object: nil)
}
}
Once postNotification is called, the function notificationReceived in class First will be called.
Create a common global data store and let all the view controllers get their data from there. This is essentially a global singleton with some accompanying functions. I know you wanted to do this without global variables but I think you should consider this.
Create a class to contain the data. Also let it be able to reload the data.
class MyData {
static let shared = MyData()
var data : SomeDataType
func loadData() {
// Load the data
}
}
Register to receive the notification as follows:
static let dataChangedNotification = Notification.Name("DataChanged")
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Establish a way for call activity to notify this class so it can update accordingly
NotificationCenter.default.addObserver(self, selector: #selector(handleDataChangedNotification(notification:)), name: "DataChanged", object: nil)
}
func handleDataChangedNotification(notification: NSNotification) {
// This ViewController was notified that data was changed
// Do something
}
func getDataToDisplay() {
let currentData = MyData.shared.data
// do something
}
// Any view controller would call this function if it changes the data
func sendDataChangeNotification() {
let obj = [String]() // make some obj to send. Pass whatever custom data you need to send
NotificationCenter.default.post(name: type(of: self).dataChangedNotification, object: obj)
}

Inter-thread inter-object communication in Swift 3 with Cocoa

My program consists of three relatively-distinct areas: listening on a network for new state, performing network actions, and updating the UI. So respectively I want three classes: StateListener, ActionSender, and ViewController, each chugging along on separate threads.
Would that it were so simple -- the three need to interact. Some states discovered by the StateListener require Actions to be sent by the ActionSender or the UI to be updated by the ViewController. Some responses to Actions require the UI to be updated by the ViewController. Some UI actions require Actions to be performed by the ActionSender.
Currently I do something like this (pseudocode):
/* ViewController.swift */
class ViewController : blah
{
//...
func buttonPressed()
{
// ?! Need to do an action here but I can't
// because actionSender is initialised below...
}
func viewDidLoad()
{
let actionSender = ActionSender(m_view: self)
let actionQueue = OperationQueue()
let stateListener = StateListener(m_view: self,
m_actionSender: actionSender,
m_actionQueue: actionQueue)
let stateQueue = OperationQueue()
stateQueue.addOperation(stateQueue.listen())
}
}
/* StateListener.swift */
class StateListener
{
// ...
func listen()
{
while true
{
var state = waitForNewState()
if shouldActOn(state)
{
m_actionQueue.addOperation(m_actionSender.act())
}
}
}
}
/* ActionSender.swift */
class ActionSender
{
// ...
func act()
{
var reply = sendAction()
OperationQueue.main.addOperation(m_view.m_textBox.append(reply))
}
}
This is fairly hellish and doesn't even do what I want it to do, because I can't have the ViewController perform actions (ActionSender's require a ViewController reference to update the view after the action, but I tried initialising the ActionSender within ViewController.init and I got bizarre errors to do with a Code.init that I hadn't implemented...). I want to get above ViewController and initialise all these OperationQueues and objects wherever ViewController gets initialised, but I can't find where that is...
What I've done above is basically object-reference injection of each object and OperationQueue. I know there are other ways of doing this (a hierarchy of callbacks, NSNotifications) but I'm unsure of which is best.
My question is in two parts:
What is the best (i.e., fastest, easiest to implement and maintain, most idiomatic in Swift) way to get the inter-object and inter-thread communication I desire?
I currently get things going from ViewController's viewDidLoad function, which seems awful (and means I can't get a 'higher-up' perspective of the ViewController. Where should this stuff go? AppDelegate advertises itself as the 'program startup' area, but I can't access the ViewController from there... XCode seems to have hidden the startup of my app from me!
I really appreciate your responses!
This post greatly helped with question 2 and the problem of passing self as a parameter to a data member in an initialiser : http://blog.scottlogic.com/2014/11/20/swift-initialisation.html
Specifically, I can use this pattern:
class Foo : blah
{
var m_bar : Bar!
init() {
// notice I get away with not initialising m_bar
}
func viewDidLoad {
m_bar = Bar(m_foo: self)
m_bar.doYourThing()
}
}
The blog prefers the following, which I feel I should add here out of gratitude to the author, though I prefer the above.
class Foo : blah
{
lazy var m_bar : Bar = {
return Bar(m_foo: self) // notice I can pass in self
}
init() {
// notice I get away with not initialising m_bar
}
func viewDidLoad {
m_bar.doYourThing()
}
}

How to use the delegates with NSKeyedUnarchiver?

I am using NSKeyedUnarchiver to unarchive an object and would like to use the delegates (NSKeyedUnarchiverDelegate), but my delegates are not called. Archiving and Unarchiving is working fine, but the Delegates (unarchiver & unarchiverDidFinish) are not called. Can someone help?
I have the following implementation:
class BlobHandler: NSObject , NSKeyedUnarchiverDelegate{
func load() -> MYOBJECTCLASS{
let data:NSData? = getBlob();
var mykeyedunarchiver:NSKeyedUnarchiver=NSKeyedUnarchiver(forReadingWithData: data!);
mykeyedunarchiver.delegate = self;
let temp=mykeyedunarchiver.decodeObjectForKey("rootobject")
// No delegates are called
if temp==nil {
blobsexists=false;
}else{
objectreturn = temp! as! MYOBJECTCLASS;
return objectreturn;
}
}
func save1(myobject:MYOBJECTCLASS){
let data = NSMutableData()
var keyedarchiver:NSKeyedArchiver=NSKeyedArchiver(forWritingWithMutableData: data);
keyedarchiver.encodeObject(maptheme, forKey: "rootobject");
let bytes = data.bytes;
let len=data.length;
saveblob(bytes);
}
The following delegates, which are also implemented in my Blobhandler, are never called:
func unarchiver(unarchiver: NSKeyedUnarchiver, cannotDecodeObjectOfClassName name: String, originalClasses classNames: [String]) -> AnyClass? {
print("I am in unarchiver !");
return nil;
}
func unarchiverDidFinish(_ unarchiver: NSKeyedUnarchiver){
print("I am in unarchiverDidFinish ! ");
}
I don't know what it was, but its working after a clean and rebuild of the project.
I notice with different cases, that the builds are not in sync sometimes. There is sometimes code, which is in XCode but it is not executed. Sounds unbelievable, but I guess its true.
XCode 7.2
I think the first function is never called since you didn't actually feed a "cannotDecodeObjectOfClassName" at all, since you only did try to unarchive previously archived data. You can try this method(or something requires a class name) to validate your solution(feed a class doesn't conform NSCoding):
unarchiver.decodeObjectOfClass(cls: NSCoding.Protocol, forKey: String)
The second one is a little bit tricky. I've tried this method in a similar situation and it turned out that unarchiverDidFinish only get called when a complete unarchiving job is done and probably before it's destroyed. For example, I had a NSCoding class and the convenience initiator is like
required convenience init?(coder aDecoder: NSCoder) {
let unarchiver = aDecoder as! NSKeyedUnarchiver
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
unarchiver.delegate = appDelegate.uad
let name = unarchiver.decodeObjectForKey(PropertyKey.nameKey) as! String
print(321)
self.init(name: name, photo: photo, rating: rating)
}
uad is an instance of class:
class UAD:NSObject, NSKeyedUnarchiverDelegate {
func unarchiverDidFinish(unarchiver: NSKeyedUnarchiver) {
print(123)
}
}
And in the view controller the loading process is like
func load() -> [User]? {
print(1)
let ret = NSKeyedUnarchiver.unarchiveObjectWithFile(ArchiveURL.path!) as? [User]
print(2)
return ret
}
And the output is like:
1
321
321
321
321
321
123
2
After finishing loading a group of users, the unarchiverDidFinish finally got called once. Notice that this is a class function and an anonymous instance is created to finish this sentence:
NSKeyedUnarchiver.unarchiveObjectWithFile(ArchiveURL.path!) as? [User]
So I really believe that this function only get called before it is destroyed or a group of call back functions is finished.
I am not quite sure if this is the case for you. You may try to make your unarchiver object global and destroy it after your loading is done to see whether this function is called.
Correct me if anything not right.
To make either unarchiverWillFinish: and unarchiverDidFinish: be called properly, we have to invoke finishDecoding when finished decoding.
Once you have the configured decoder object, to decode an object or data item, use the decodeObjectForKey: method. When finished decoding a keyed archive, you should invoke finishDecoding before releasing the unarchiver.
We notify the delegate of the instance of NSKeyedUnarchiver and perform any final operations on the archive through invoking this method. And once this method is invoked, according to Apple's official documentation, our unarchiver cannot decode any further values. We would get following message if we continue to perform any decoding operation after invoked finishDecoding:
*** -[NSKeyedUnarchiver decodeObjectForKey:]: unarchive already finished, cannot decode anything more
It also makes sense for encoding counterparts.