MVVM in Swift iOS The 2019 Stack Overflow Developer Survey Results Are In Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)Networking in iOS SwiftGetting WiFi SSID on iOS in SwiftSwift iOS - Call back functionsOptimize Data Sending/Querying to Parse Swift iOSRxSwift iOS Observable FIFO Queue Implementation in Swift 2Navigation controller in SwiftSwift/iOS: Subclassing UILabel and setting propertiesSwift/iOS component for label with clickable text buttonsiOS Memory Game - Swift 4Networking structure for Swift iOS app
When did F become S? Why?
Separating matrix elements by lines
Sub-subscripts in strings cause different spacings than subscripts
Was credit for the black hole image misappropriated?
My body leaves; my core can stay
Why did Peik Lin say, "I'm not an animal"?
how can a perfect fourth interval be considered either consonant or dissonant?
Why did they expect Astronaut Scott Kelley's telomere shortening to accelerate? (they got longer!)
Student Loan from years ago pops up and is taking my salary
Could an empire control the whole planet with today's comunication methods?
What information about me do stores get via my credit card?
Example of compact Riemannian manifold with only one geodesic.
What is the role of 'For' here?
One-dimensional Japanese puzzle
Would an alien lifeform be able to achieve space travel if lacking in vision?
"... to apply for a visa" or "... and applied for a visa"?
Why can't devices on different VLANs, but on the same subnet, communicate?
Is it ok to offer lower paid work as a trial period before negotiating for a full-time job?
Didn't get enough time to take a Coding Test - what to do now?
Accepted by European university, rejected by all American ones I applied to? Possible reasons?
Can the DM override racial traits?
ELI5: Why do they say that Israel would have been the fourth country to land a spacecraft on the Moon and why do they call it low cost?
Why are PDP-7-style microprogrammed instructions out of vogue?
Make it rain characters
MVVM in Swift iOS
The 2019 Stack Overflow Developer Survey Results Are In
Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)Networking in iOS SwiftGetting WiFi SSID on iOS in SwiftSwift iOS - Call back functionsOptimize Data Sending/Querying to Parse Swift iOSRxSwift iOS Observable FIFO Queue Implementation in Swift 2Navigation controller in SwiftSwift/iOS: Subclassing UILabel and setting propertiesSwift/iOS component for label with clickable text buttonsiOS Memory Game - Swift 4Networking structure for Swift iOS app
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
I've been implementing MVVM in Swift. I've looked at several implementations, many of which violate some aspects of MVVM and wanted to have a go with my own version that contains a Web request service.
View:
class BreachView: UIView
var nameLabel = UILabel()
public override init(frame: CGRect)
let labelframe = CGRect(x: 0, y: 50, width: frame.width, height: 20)
nameLabel.frame = labelframe
nameLabel.backgroundColor = .gray
super.init(frame: frame)
self.addSubview(nameLabel)
backgroundColor = .red
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ViewController
class ViewController: UIViewController
var breachesViewModel: BreachViewModelType!
var breachView : BreachView?
// to be called during testing
init(viewModel: BreachViewModelType)
breachesViewModel = viewModel
super.init(nibName: nil, bundle: nil)
// required when called from storyboard
required init?(coder aDecoder: NSCoder)
breachesViewModel = BreachViewModel()
super.init(coder: aDecoder)
override func viewDidLoad()
super.viewDidLoad()
breachesViewModel.fetchData [weak self] breaches in
guard let self = self else return
DispatchQueue.main.async
self.updateUI()
func updateUI()
breachView = BreachView(frame: view.frame)
breachesViewModel.configure(breachView!, number: 3)
view.addSubview(breachView!)
Protocol for dependency injection:
protocol BreachViewModelType
func fetchData(completion: @escaping ([BreachModel]) -> Void)
func configure (_ view: BreachView, number index: Int)
ViewModel
class BreachViewModel : BreachViewModelType
var breaches = BreachModel
init()
// add init for ClosureHTTPManager here, to allow it to be teestable in the future
func fetchData(completion: @escaping ([BreachModel]) -> Void)
ClosureHTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: [weak self] result in
guard let self = self else return
switch result
case .failure(let error):
print ("failure", error)
case .success(let dta) :
let decoder = JSONDecoder()
do
self.breaches = try decoder.decode([BreachModel].self, from: dta)
completion(try decoder.decode([BreachModel].self, from: dta))
catch
// deal with error from JSON decoding!
)
func numberItemsToDisplay() -> Int
return breaches.count
func configure (_ view: BreachView, number index: Int)
// set the name and data in the view
view.nameLabel.text = breaches[index].name
and HTTP manager
class ClosureHTTPManager
static let shared: ClosureHTTPManager = ClosureHTTPManager()
enum HTTPError: Error
case invalidURL
case invalidResponse(Data?, URLResponse?)
public func get(urlString: String, completionBlock: @escaping (Result<Data, Error>) -> Void)
guard let url = URL(string: urlString) else
completionBlock(.failure(HTTPError.invalidURL))
return
let task = URLSession.shared.dataTask(with: url) data, response, error in
guard error == nil else
completionBlock(.failure(error!))
return
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode else
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
completionBlock(.success(responseData))
task.resume()
Calling the API from
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Any comments on whether the implementation conforms to MVVM or not, typos, changes etc. are appreciated.
Git link: https://github.com/stevencurtis/MVVMWithNetworkService
swift
$endgroup$
add a comment |
$begingroup$
I've been implementing MVVM in Swift. I've looked at several implementations, many of which violate some aspects of MVVM and wanted to have a go with my own version that contains a Web request service.
View:
class BreachView: UIView
var nameLabel = UILabel()
public override init(frame: CGRect)
let labelframe = CGRect(x: 0, y: 50, width: frame.width, height: 20)
nameLabel.frame = labelframe
nameLabel.backgroundColor = .gray
super.init(frame: frame)
self.addSubview(nameLabel)
backgroundColor = .red
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ViewController
class ViewController: UIViewController
var breachesViewModel: BreachViewModelType!
var breachView : BreachView?
// to be called during testing
init(viewModel: BreachViewModelType)
breachesViewModel = viewModel
super.init(nibName: nil, bundle: nil)
// required when called from storyboard
required init?(coder aDecoder: NSCoder)
breachesViewModel = BreachViewModel()
super.init(coder: aDecoder)
override func viewDidLoad()
super.viewDidLoad()
breachesViewModel.fetchData [weak self] breaches in
guard let self = self else return
DispatchQueue.main.async
self.updateUI()
func updateUI()
breachView = BreachView(frame: view.frame)
breachesViewModel.configure(breachView!, number: 3)
view.addSubview(breachView!)
Protocol for dependency injection:
protocol BreachViewModelType
func fetchData(completion: @escaping ([BreachModel]) -> Void)
func configure (_ view: BreachView, number index: Int)
ViewModel
class BreachViewModel : BreachViewModelType
var breaches = BreachModel
init()
// add init for ClosureHTTPManager here, to allow it to be teestable in the future
func fetchData(completion: @escaping ([BreachModel]) -> Void)
ClosureHTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: [weak self] result in
guard let self = self else return
switch result
case .failure(let error):
print ("failure", error)
case .success(let dta) :
let decoder = JSONDecoder()
do
self.breaches = try decoder.decode([BreachModel].self, from: dta)
completion(try decoder.decode([BreachModel].self, from: dta))
catch
// deal with error from JSON decoding!
)
func numberItemsToDisplay() -> Int
return breaches.count
func configure (_ view: BreachView, number index: Int)
// set the name and data in the view
view.nameLabel.text = breaches[index].name
and HTTP manager
class ClosureHTTPManager
static let shared: ClosureHTTPManager = ClosureHTTPManager()
enum HTTPError: Error
case invalidURL
case invalidResponse(Data?, URLResponse?)
public func get(urlString: String, completionBlock: @escaping (Result<Data, Error>) -> Void)
guard let url = URL(string: urlString) else
completionBlock(.failure(HTTPError.invalidURL))
return
let task = URLSession.shared.dataTask(with: url) data, response, error in
guard error == nil else
completionBlock(.failure(error!))
return
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode else
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
completionBlock(.success(responseData))
task.resume()
Calling the API from
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Any comments on whether the implementation conforms to MVVM or not, typos, changes etc. are appreciated.
Git link: https://github.com/stevencurtis/MVVMWithNetworkService
swift
$endgroup$
add a comment |
$begingroup$
I've been implementing MVVM in Swift. I've looked at several implementations, many of which violate some aspects of MVVM and wanted to have a go with my own version that contains a Web request service.
View:
class BreachView: UIView
var nameLabel = UILabel()
public override init(frame: CGRect)
let labelframe = CGRect(x: 0, y: 50, width: frame.width, height: 20)
nameLabel.frame = labelframe
nameLabel.backgroundColor = .gray
super.init(frame: frame)
self.addSubview(nameLabel)
backgroundColor = .red
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ViewController
class ViewController: UIViewController
var breachesViewModel: BreachViewModelType!
var breachView : BreachView?
// to be called during testing
init(viewModel: BreachViewModelType)
breachesViewModel = viewModel
super.init(nibName: nil, bundle: nil)
// required when called from storyboard
required init?(coder aDecoder: NSCoder)
breachesViewModel = BreachViewModel()
super.init(coder: aDecoder)
override func viewDidLoad()
super.viewDidLoad()
breachesViewModel.fetchData [weak self] breaches in
guard let self = self else return
DispatchQueue.main.async
self.updateUI()
func updateUI()
breachView = BreachView(frame: view.frame)
breachesViewModel.configure(breachView!, number: 3)
view.addSubview(breachView!)
Protocol for dependency injection:
protocol BreachViewModelType
func fetchData(completion: @escaping ([BreachModel]) -> Void)
func configure (_ view: BreachView, number index: Int)
ViewModel
class BreachViewModel : BreachViewModelType
var breaches = BreachModel
init()
// add init for ClosureHTTPManager here, to allow it to be teestable in the future
func fetchData(completion: @escaping ([BreachModel]) -> Void)
ClosureHTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: [weak self] result in
guard let self = self else return
switch result
case .failure(let error):
print ("failure", error)
case .success(let dta) :
let decoder = JSONDecoder()
do
self.breaches = try decoder.decode([BreachModel].self, from: dta)
completion(try decoder.decode([BreachModel].self, from: dta))
catch
// deal with error from JSON decoding!
)
func numberItemsToDisplay() -> Int
return breaches.count
func configure (_ view: BreachView, number index: Int)
// set the name and data in the view
view.nameLabel.text = breaches[index].name
and HTTP manager
class ClosureHTTPManager
static let shared: ClosureHTTPManager = ClosureHTTPManager()
enum HTTPError: Error
case invalidURL
case invalidResponse(Data?, URLResponse?)
public func get(urlString: String, completionBlock: @escaping (Result<Data, Error>) -> Void)
guard let url = URL(string: urlString) else
completionBlock(.failure(HTTPError.invalidURL))
return
let task = URLSession.shared.dataTask(with: url) data, response, error in
guard error == nil else
completionBlock(.failure(error!))
return
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode else
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
completionBlock(.success(responseData))
task.resume()
Calling the API from
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Any comments on whether the implementation conforms to MVVM or not, typos, changes etc. are appreciated.
Git link: https://github.com/stevencurtis/MVVMWithNetworkService
swift
$endgroup$
I've been implementing MVVM in Swift. I've looked at several implementations, many of which violate some aspects of MVVM and wanted to have a go with my own version that contains a Web request service.
View:
class BreachView: UIView
var nameLabel = UILabel()
public override init(frame: CGRect)
let labelframe = CGRect(x: 0, y: 50, width: frame.width, height: 20)
nameLabel.frame = labelframe
nameLabel.backgroundColor = .gray
super.init(frame: frame)
self.addSubview(nameLabel)
backgroundColor = .red
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
ViewController
class ViewController: UIViewController
var breachesViewModel: BreachViewModelType!
var breachView : BreachView?
// to be called during testing
init(viewModel: BreachViewModelType)
breachesViewModel = viewModel
super.init(nibName: nil, bundle: nil)
// required when called from storyboard
required init?(coder aDecoder: NSCoder)
breachesViewModel = BreachViewModel()
super.init(coder: aDecoder)
override func viewDidLoad()
super.viewDidLoad()
breachesViewModel.fetchData [weak self] breaches in
guard let self = self else return
DispatchQueue.main.async
self.updateUI()
func updateUI()
breachView = BreachView(frame: view.frame)
breachesViewModel.configure(breachView!, number: 3)
view.addSubview(breachView!)
Protocol for dependency injection:
protocol BreachViewModelType
func fetchData(completion: @escaping ([BreachModel]) -> Void)
func configure (_ view: BreachView, number index: Int)
ViewModel
class BreachViewModel : BreachViewModelType
var breaches = BreachModel
init()
// add init for ClosureHTTPManager here, to allow it to be teestable in the future
func fetchData(completion: @escaping ([BreachModel]) -> Void)
ClosureHTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: [weak self] result in
guard let self = self else return
switch result
case .failure(let error):
print ("failure", error)
case .success(let dta) :
let decoder = JSONDecoder()
do
self.breaches = try decoder.decode([BreachModel].self, from: dta)
completion(try decoder.decode([BreachModel].self, from: dta))
catch
// deal with error from JSON decoding!
)
func numberItemsToDisplay() -> Int
return breaches.count
func configure (_ view: BreachView, number index: Int)
// set the name and data in the view
view.nameLabel.text = breaches[index].name
and HTTP manager
class ClosureHTTPManager
static let shared: ClosureHTTPManager = ClosureHTTPManager()
enum HTTPError: Error
case invalidURL
case invalidResponse(Data?, URLResponse?)
public func get(urlString: String, completionBlock: @escaping (Result<Data, Error>) -> Void)
guard let url = URL(string: urlString) else
completionBlock(.failure(HTTPError.invalidURL))
return
let task = URLSession.shared.dataTask(with: url) data, response, error in
guard error == nil else
completionBlock(.failure(error!))
return
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode else
completionBlock(.failure(HTTPError.invalidResponse(data, response)))
return
completionBlock(.success(responseData))
task.resume()
Calling the API from
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Any comments on whether the implementation conforms to MVVM or not, typos, changes etc. are appreciated.
Git link: https://github.com/stevencurtis/MVVMWithNetworkService
swift
swift
asked 5 mins ago
stevenpcurtisstevenpcurtis
1227
1227
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217371%2fmvvm-in-swift-ios%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217371%2fmvvm-in-swift-ios%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown