Networking structure for Swift iOS app
$begingroup$
I would like any constructive comments regarding the structure of this simple App that takes an API response and then displays on a table view.
The URL is written in a ConstantsAPI file
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Displayed on a tableviewcontroller
class SitewideTableViewController: UITableViewController, DataManagerDelegate {
var pwnedData = [BreachModel]()
var session: URLSession!
var task: URLSessionDownloadTask!
override func viewDidLoad() {
super.viewDidLoad()
session = URLSession.shared
task = URLSessionDownloadTask()
DataManager.shared.delegate = self
DataManager.shared.fetchBreaches()
}
func didDownloadBreaches() {
DispatchQueue.main.async {
self.pwnedData = DataManager.shared.sortedBreaches()
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pwnedData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Sitewide", for: indexPath)
cell.textLabel?.text = pwnedData[indexPath.row].name
return cell
}
}
Using the following model
import Foundation
class BreachModel : Codable {
let name : String
let title : String
let domain : String
let breachDate : String
let addedDate : String
let modifiedData : String
let pwnCount : Int
let description: String
private enum CodingKeys: String, CodingKey {
case name = "Name"
case title = "Title"
case domain = "Domain"
case breachDate = "BreachDate"
case addedDate = "AddedDate"
case modifiedData = "ModifiedDate"
case pwnCount = "PwnCount"
case description = "Description"
}
}
With a Data manager that would manage all of the data
@objc protocol DataManagerDelegate: class {
// optional delegate to practice
@objc optional func didDownloadBreaches() // called when the manager has completed downloading all the breaches
}
class DataManager {
static let shared: DataManager = DataManager()
public weak var delegate: DataManagerDelegate? = nil
private var breaches = [BreachModel]()
func fetchBreaches() {
HTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: { [weak self] (data: Data?) -> Void in
let decoder = JSONDecoder()
if let data = data{
print(data.count)
do {
self?.breaches = try decoder.decode([BreachModel].self, from: data)
self?.delegate?.didDownloadBreaches?()
} catch let error {
print ("Error in reading data", error)
}
}
}
)
}
func sortedBreaches() -> [BreachModel] {
return breaches.sorted{ a,b in a.name < b.name }
}
}
That calls a HTTP manager whose only responsibility is to call url's
class HTTPManager {
static let shared: HTTPManager = HTTPManager()
public func get (urlString: String, completionBlock: ((Data?) -> Void)?) {
let url = URL(string: urlString)
if let usableUrl = url {
let request = URLRequest(url: usableUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
completionBlock?(data)
})
task.resume()
}
}
}
So is this a reasonable extendable structure?
Should I have used dataTask or URLSessionDownloadTask?
Have I unwittingly introduced some memory leaks?
Any comments appreciated, I'm familiar with the Swift book but find other tutorials tend to just show the use of codable or an API and do not talk through the whole structure (at least not at an appropriate level for me). The code above does work, and I'm thinking of building upon it in the future but want to follow some form of best practice (no matter how trivial).
Git link: https://github.com/stevencurtis/basicnetworking
swift ios
$endgroup$
add a comment |
$begingroup$
I would like any constructive comments regarding the structure of this simple App that takes an API response and then displays on a table view.
The URL is written in a ConstantsAPI file
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Displayed on a tableviewcontroller
class SitewideTableViewController: UITableViewController, DataManagerDelegate {
var pwnedData = [BreachModel]()
var session: URLSession!
var task: URLSessionDownloadTask!
override func viewDidLoad() {
super.viewDidLoad()
session = URLSession.shared
task = URLSessionDownloadTask()
DataManager.shared.delegate = self
DataManager.shared.fetchBreaches()
}
func didDownloadBreaches() {
DispatchQueue.main.async {
self.pwnedData = DataManager.shared.sortedBreaches()
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pwnedData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Sitewide", for: indexPath)
cell.textLabel?.text = pwnedData[indexPath.row].name
return cell
}
}
Using the following model
import Foundation
class BreachModel : Codable {
let name : String
let title : String
let domain : String
let breachDate : String
let addedDate : String
let modifiedData : String
let pwnCount : Int
let description: String
private enum CodingKeys: String, CodingKey {
case name = "Name"
case title = "Title"
case domain = "Domain"
case breachDate = "BreachDate"
case addedDate = "AddedDate"
case modifiedData = "ModifiedDate"
case pwnCount = "PwnCount"
case description = "Description"
}
}
With a Data manager that would manage all of the data
@objc protocol DataManagerDelegate: class {
// optional delegate to practice
@objc optional func didDownloadBreaches() // called when the manager has completed downloading all the breaches
}
class DataManager {
static let shared: DataManager = DataManager()
public weak var delegate: DataManagerDelegate? = nil
private var breaches = [BreachModel]()
func fetchBreaches() {
HTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: { [weak self] (data: Data?) -> Void in
let decoder = JSONDecoder()
if let data = data{
print(data.count)
do {
self?.breaches = try decoder.decode([BreachModel].self, from: data)
self?.delegate?.didDownloadBreaches?()
} catch let error {
print ("Error in reading data", error)
}
}
}
)
}
func sortedBreaches() -> [BreachModel] {
return breaches.sorted{ a,b in a.name < b.name }
}
}
That calls a HTTP manager whose only responsibility is to call url's
class HTTPManager {
static let shared: HTTPManager = HTTPManager()
public func get (urlString: String, completionBlock: ((Data?) -> Void)?) {
let url = URL(string: urlString)
if let usableUrl = url {
let request = URLRequest(url: usableUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
completionBlock?(data)
})
task.resume()
}
}
}
So is this a reasonable extendable structure?
Should I have used dataTask or URLSessionDownloadTask?
Have I unwittingly introduced some memory leaks?
Any comments appreciated, I'm familiar with the Swift book but find other tutorials tend to just show the use of codable or an API and do not talk through the whole structure (at least not at an appropriate level for me). The code above does work, and I'm thinking of building upon it in the future but want to follow some form of best practice (no matter how trivial).
Git link: https://github.com/stevencurtis/basicnetworking
swift ios
$endgroup$
add a comment |
$begingroup$
I would like any constructive comments regarding the structure of this simple App that takes an API response and then displays on a table view.
The URL is written in a ConstantsAPI file
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Displayed on a tableviewcontroller
class SitewideTableViewController: UITableViewController, DataManagerDelegate {
var pwnedData = [BreachModel]()
var session: URLSession!
var task: URLSessionDownloadTask!
override func viewDidLoad() {
super.viewDidLoad()
session = URLSession.shared
task = URLSessionDownloadTask()
DataManager.shared.delegate = self
DataManager.shared.fetchBreaches()
}
func didDownloadBreaches() {
DispatchQueue.main.async {
self.pwnedData = DataManager.shared.sortedBreaches()
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pwnedData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Sitewide", for: indexPath)
cell.textLabel?.text = pwnedData[indexPath.row].name
return cell
}
}
Using the following model
import Foundation
class BreachModel : Codable {
let name : String
let title : String
let domain : String
let breachDate : String
let addedDate : String
let modifiedData : String
let pwnCount : Int
let description: String
private enum CodingKeys: String, CodingKey {
case name = "Name"
case title = "Title"
case domain = "Domain"
case breachDate = "BreachDate"
case addedDate = "AddedDate"
case modifiedData = "ModifiedDate"
case pwnCount = "PwnCount"
case description = "Description"
}
}
With a Data manager that would manage all of the data
@objc protocol DataManagerDelegate: class {
// optional delegate to practice
@objc optional func didDownloadBreaches() // called when the manager has completed downloading all the breaches
}
class DataManager {
static let shared: DataManager = DataManager()
public weak var delegate: DataManagerDelegate? = nil
private var breaches = [BreachModel]()
func fetchBreaches() {
HTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: { [weak self] (data: Data?) -> Void in
let decoder = JSONDecoder()
if let data = data{
print(data.count)
do {
self?.breaches = try decoder.decode([BreachModel].self, from: data)
self?.delegate?.didDownloadBreaches?()
} catch let error {
print ("Error in reading data", error)
}
}
}
)
}
func sortedBreaches() -> [BreachModel] {
return breaches.sorted{ a,b in a.name < b.name }
}
}
That calls a HTTP manager whose only responsibility is to call url's
class HTTPManager {
static let shared: HTTPManager = HTTPManager()
public func get (urlString: String, completionBlock: ((Data?) -> Void)?) {
let url = URL(string: urlString)
if let usableUrl = url {
let request = URLRequest(url: usableUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
completionBlock?(data)
})
task.resume()
}
}
}
So is this a reasonable extendable structure?
Should I have used dataTask or URLSessionDownloadTask?
Have I unwittingly introduced some memory leaks?
Any comments appreciated, I'm familiar with the Swift book but find other tutorials tend to just show the use of codable or an API and do not talk through the whole structure (at least not at an appropriate level for me). The code above does work, and I'm thinking of building upon it in the future but want to follow some form of best practice (no matter how trivial).
Git link: https://github.com/stevencurtis/basicnetworking
swift ios
$endgroup$
I would like any constructive comments regarding the structure of this simple App that takes an API response and then displays on a table view.
The URL is written in a ConstantsAPI file
let baseUrl : String = "https://haveibeenpwned.com/api/v2"
let breachesExtensionURL : String = "/breaches"
Displayed on a tableviewcontroller
class SitewideTableViewController: UITableViewController, DataManagerDelegate {
var pwnedData = [BreachModel]()
var session: URLSession!
var task: URLSessionDownloadTask!
override func viewDidLoad() {
super.viewDidLoad()
session = URLSession.shared
task = URLSessionDownloadTask()
DataManager.shared.delegate = self
DataManager.shared.fetchBreaches()
}
func didDownloadBreaches() {
DispatchQueue.main.async {
self.pwnedData = DataManager.shared.sortedBreaches()
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return pwnedData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Sitewide", for: indexPath)
cell.textLabel?.text = pwnedData[indexPath.row].name
return cell
}
}
Using the following model
import Foundation
class BreachModel : Codable {
let name : String
let title : String
let domain : String
let breachDate : String
let addedDate : String
let modifiedData : String
let pwnCount : Int
let description: String
private enum CodingKeys: String, CodingKey {
case name = "Name"
case title = "Title"
case domain = "Domain"
case breachDate = "BreachDate"
case addedDate = "AddedDate"
case modifiedData = "ModifiedDate"
case pwnCount = "PwnCount"
case description = "Description"
}
}
With a Data manager that would manage all of the data
@objc protocol DataManagerDelegate: class {
// optional delegate to practice
@objc optional func didDownloadBreaches() // called when the manager has completed downloading all the breaches
}
class DataManager {
static let shared: DataManager = DataManager()
public weak var delegate: DataManagerDelegate? = nil
private var breaches = [BreachModel]()
func fetchBreaches() {
HTTPManager.shared.get(urlString: baseUrl + breachesExtensionURL, completionBlock: { [weak self] (data: Data?) -> Void in
let decoder = JSONDecoder()
if let data = data{
print(data.count)
do {
self?.breaches = try decoder.decode([BreachModel].self, from: data)
self?.delegate?.didDownloadBreaches?()
} catch let error {
print ("Error in reading data", error)
}
}
}
)
}
func sortedBreaches() -> [BreachModel] {
return breaches.sorted{ a,b in a.name < b.name }
}
}
That calls a HTTP manager whose only responsibility is to call url's
class HTTPManager {
static let shared: HTTPManager = HTTPManager()
public func get (urlString: String, completionBlock: ((Data?) -> Void)?) {
let url = URL(string: urlString)
if let usableUrl = url {
let request = URLRequest(url: usableUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
completionBlock?(data)
})
task.resume()
}
}
}
So is this a reasonable extendable structure?
Should I have used dataTask or URLSessionDownloadTask?
Have I unwittingly introduced some memory leaks?
Any comments appreciated, I'm familiar with the Swift book but find other tutorials tend to just show the use of codable or an API and do not talk through the whole structure (at least not at an appropriate level for me). The code above does work, and I'm thinking of building upon it in the future but want to follow some form of best practice (no matter how trivial).
Git link: https://github.com/stevencurtis/basicnetworking
swift ios
swift ios
edited 4 mins ago
stevenpcurtis
asked 9 mins ago
stevenpcurtisstevenpcurtis
1505
1505
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
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');
});
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%2f216310%2fnetworking-structure-for-swift-ios-app%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');
});
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%2f216310%2fnetworking-structure-for-swift-ios-app%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');
});
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');
});
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');
});
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