git.iko.soy/dashared/GDproject
on master help sign in
improved log in (now with numer). user-experience is improved
commitfce9ad4d226c7644dabb4c41b05efe996ab418d6 authorDarya Rednikina <rednikina.com@yandex.ru> dateMon 25 Feb 2019, 23:57 (8 years ago) parentee55c87
12 files changed+280−45
GDproject.xcodeproj/project.pbxproj+4 GDproject/Base.lproj/Main.storyboard+7−1 GDproject/Controller/ News and channels/NewPostViewController.swift+12 GDproject/Controller/ News and channels/NewsController.swift+55−12 GDproject/Controller/ News and channels/PostViewCell.swift+4 GDproject/Controller/Log In/LogInCoordinator.swift+6−4 GDproject/Controller/Log In/LogInViewController.swift+21−3 GDproject/Controller/Profile/ProfileViewController.swift+10−3 GDproject/Controller/RepeatingTimer.swift+59 GDproject/DataStorage.swift+9−14 GDproject/Simple model/Model.swift+92−7 GDproject/Supporting files/AppDelegate.swift+1−1
GDproject.xcodeproj/project.pbxproj+4
@@ -12,6 +12,7 @@
1212 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
1313 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; };
1414 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
15+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */; };
1516 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
1617 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
1718 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
@@ -75,6 +76,7 @@
7576 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
7677 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
7778 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
79+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = "<group>"; };
7880 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
7981 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
8082 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
@@ -222,6 +224,7 @@
222224 12E36DCF22145923006FCDD7 /* Controller */ = {
223225 isa = PBXGroup;
224226 children = (
227+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */,
225228 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
226229 1291BE3722218DC4009D3F23 /* Log In */,
227230 1288B5CC221F0EFE002BE6B1 /* Profile */,
@@ -343,6 +346,7 @@
343346 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
344347 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
345348 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
349+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */,
346350 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
347351 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
348352 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
GDproject/Base.lproj/Main.storyboard+7−1
@@ -387,16 +387,22 @@
387387 </textField>
388388 </subviews>
389389 </stackView>
390+ <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="K3b-gv-JPp">
391+ <rect key="frame" x="177.5" y="333.5" width="20" height="20"/>
392+ </activityIndicatorView>
390393 </subviews>
391394 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
392395 <constraints>
393396 <constraint firstItem="UwC-m5-jA5" firstAttribute="leading" secondItem="LOR-Yw-0FU" secondAttribute="leading" constant="16" id="M6z-nU-H40"/>
397+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerY" secondItem="LOR-Yw-0FU" secondAttribute="centerY" id="eOp-yQ-n28"/>
398+ <constraint firstItem="K3b-gv-JPp" firstAttribute="centerX" secondItem="LOR-Yw-0FU" secondAttribute="centerX" id="gdW-VU-pE8"/>
394399 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
395400 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
396401 </constraints>
397402 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
398403 </view>
399404 <connections>
405+ <outlet property="indicatorView" destination="K3b-gv-JPp" id="mSR-QU-VZQ"/>
400406 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
401407 </connections>
402408 </viewController>
@@ -437,7 +443,7 @@
437443 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
438444 </connections>
439445 </tableView>
440− <navigationItem key="navigationItem" id="b8E-6f-hLM"/>
446+ <navigationItem key="navigationItem" largeTitleDisplayMode="always" id="b8E-6f-hLM"/>
441447 </tableViewController>
442448 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
443449 </objects>
GDproject/Controller/ News and channels/NewPostViewController.swift+12
@@ -49,11 +49,22 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
4949 Model.createDraft { [weak self] (int) in
5050 self?.indexOfPost = int
5151 }
52+ //createTimerForUpdates()
53+
5254 setUpMD()
5355 setUpTextView()
5456 setUpAccessoryView()
5557 }
5658
59+ func createTimerForUpdates(){
60+ // timer for update
61+ let t = RepeatingTimer(timeInterval: 15)
62+ t.eventHandler = { [unowned self] in
63+ Model.update(post: Model.Posts(body: [Model.Attachments(markdown: self.textView.text)], authorId: 42, id: self.indexOfPost))
64+ }
65+ t.resume()
66+ }
67+
5768 override func viewWillAppear(_ animated: Bool) {
5869 super.viewWillAppear(animated)
5970
@@ -181,6 +192,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
181192 let layoutManager = NSLayoutManager()
182193
183194 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
195+ //textStorage.addLayoutManager(textView.layoutManager)
184196 textStorage.addLayoutManager(layoutManager)
185197
186198 let textContainer = NSTextContainer()
GDproject/Controller/ News and channels/NewsController.swift+55−12
@@ -25,11 +25,32 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
2525 self.channel = channel
2626 }
2727
28+ var dictionary: [Int: Model.Users]? {
29+ didSet{
30+
31+ var newPosts: [Model.Posts] = []
32+
33+ channel?.posts.forEach({ (post) in
34+ newPosts.append(Model.Posts(body: post.body, authorId: post.authorId, id: post.id, user: dictionary![post.authorId]!))
35+ })
36+
37+
38+ news.dataSourse = newPosts
39+ tableView.reloadData()
40+ }
41+ }
42+
2843 var channel: Channel?{
2944 didSet{
30− news.dataSourse = channel?.posts ?? []
45+ let hh = channel?.posts.map({ (post) -> Int in
46+ post.authorId
47+ })
48+
3149 navigationItem.title = channel?.title ?? ""
32− tableView.reloadData()
50+ Model.getUsers(for: hh ?? []) { [weak self] (dict) in
51+ self?.dictionary = dict
52+ self?.refreshContr?.endRefreshing()
53+ }
3354 }
3455 }
3556
@@ -44,22 +65,29 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
4465
4566 var news = NewsVC()
4667
68+ var refreshContr: UIRefreshControl? = {
69+ let rc = UIRefreshControl()
70+ rc.addTarget(self, action: #selector(refreshPostsData(_:)), for: .valueChanged)
71+ return rc
72+ }()
73+
4774 override func viewDidLoad()
4875 {
4976 super.viewDidLoad()
77+ navigationItem.title = "Loading ..."
78+ tableView.refreshControl = refreshContr
79+ // Configure Refresh Control
5080
81+ Model.getLast { [weak self] (channel) in
82+ self?.channel = channel
83+ }
5184
5285 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
5386 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
5487
55− searchController.delegate = self
56− searchController.obscuresBackgroundDuringPresentation = false
57− navigationItem.searchController = searchController
58− definesPresentationContext = true
59− searchController.searchBar.placeholder = "Search anything"
88+ setUpSearchContr()
6089
6190 news.viewController = self
62−
6391 news.type = .NONE
6492
6593 setUpNavigationItemsforPosts()
@@ -67,6 +95,23 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
6795 //setUpBanner()
6896 }
6997
98+ func setUpSearchContr(){
99+ searchController.delegate = self
100+ searchController.obscuresBackgroundDuringPresentation = false
101+ navigationItem.searchController = searchController
102+ definesPresentationContext = true
103+ searchController.searchBar.placeholder = "Search anything"
104+ }
105+
106+ @objc func refreshPostsData( _ ff: UIRefreshControl){
107+ if ff.isRefreshing {
108+ Model.getLast { [weak self] (channel) in
109+ self?.channel = channel
110+ self?.refreshContr?.endRefreshing()
111+ }
112+ }
113+ }
114+
70115 deinit {
71116 print("news clear")
72117 }
@@ -86,11 +131,9 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
86131 searchController.isActive = false
87132 // TODO:- display something if no posts are availiable
88133
89− Model.getLast { (channel) in
90− self.channel = channel
91− }
134+ refreshContr?.beginRefreshing()
92135
93− tableView.reloadData()
136+ // tableView.reloadData()
94137 }
95138
96139 func setUpNavigationItemsforPosts(){
GDproject/Controller/ News and channels/PostViewCell.swift+4
@@ -83,8 +83,12 @@ class PostViewCell: UITableViewCell
8383 {
8484 views.append(createTextView(with: attachment.markdown))
8585 }
86+
87+ nameLabel.text = "\(post.user?.firstName ?? "") \(post.user?.secondName ?? "")"
88+ fullNameLabel.text = "\(post.authorId)"
8689 setUpInStackView(isFullVersoin)
8790 }
91+
8892 let hashtags = ["Kek", "Lol"]
8993 func setUpInStackView(_ full : Bool){
9094 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
GDproject/Controller/Log In/LogInCoordinator.swift+6−4
@@ -30,11 +30,13 @@ class LogInCoordinator{
3030
3131 private func showLogInPage(){
3232 let controller = UIStoryboard.makeLogIn()
33− controller.onLogIn = { [weak self] in
34− DataStorage.standard.setIsLoggedIn(value: true)
35− Model.authenticate()
33+ controller.onLogIn = {
34+ (id) in
35+ Model.authenticate(with: id) {
36+ (res) in controller.authenticateSucceeded = res
37+ }
3638 }
37− //navigationController?.setViewControllers([controller], animated: false)
39+
3840 navigationController?.pushViewController(controller, animated: false)
3941 }
4042
GDproject/Controller/Log In/LogInViewController.swift+21−3
@@ -12,8 +12,18 @@ class LogInViewController: UIViewController {
1212
1313 @IBOutlet weak var mailTextField: UITextField!
1414
15− var onLogIn: (()->())?
15+ @IBOutlet weak var indicatorView: UIActivityIndicatorView!
1616
17+ var onLogIn: ((Int)->())?
18+
19+ var authenticateSucceeded: Bool? {
20+ didSet {
21+ if !authenticateSucceeded! {
22+ indicatorView.stopAnimating()
23+ indicatorView.isHidden = true
24+ }
25+ }
26+ }
1727 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
1828
1929 var bottomConstraint: NSLayoutConstraint?
@@ -64,12 +74,20 @@ class LogInViewController: UIViewController {
6474 @objc func activateLogInProcess(){
6575 if logInButton.isEnabled {
6676 // MARK:- when log in is succeeded do I need to go there?
67− onLogIn?()
68− view.endEditing(true)
77+ if let id = Int(mailTextField.text!){
78+ indicatorView.isHidden = false
79+ indicatorView.startAnimating()
80+ onLogIn?(id)
81+ view.endEditing(true)
82+ } else {
83+ logInButton.isEnabled = false
84+ logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal)
85+ }
6986 }
7087 }
7188
7289 func setUpView(){
90+ indicatorView.isHidden = true
7391 mailTextField.delegate = self
7492 view.addSubview(keyboardBar)
7593
GDproject/Controller/Profile/ProfileViewController.swift+10−3
@@ -44,13 +44,20 @@ class ProfileViewController: UIViewController
4444 var basicInfo = BasicInfoController()
4545 var posts = NewsVC()
4646
47+ var dataSourse: [Model.Posts]?{
48+ didSet{
49+ self.posts.dataSourse = dataSourse!
50+ tableView.reloadData()
51+ }
52+ }
53+
4754 override func viewDidLoad()
4855 {
4956 super.viewDidLoad()
5057
51− //posts.dataSourse = [Post(dataArray: [.text("Le Lorem Ipsum est simplement du faux texte employé dans la composition et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, quand un imprimeur anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. comme Aldus PageMaker. est qu'il possède une distribution de lettres plus ou moins normale, et en tout cas comparable avec celle du français standard. De nombreuses suites logicielles de mise en page ou éditeurs de sites Web ont fait du Lorem Ipsum leur faux texte par défaut, et une recherche pour 'Lorem Ipsum' vous conduira vers de nombreux sites qui n'en sont encore qu'à leur phase de construction. Plusieurs versions sont apparues avec le temps, parfois par accident, souvent intentionnellement (histoire d'y rajouter de petits clins d'oeil, voire des phrases embarassantes).")]), Post(dataArray: [.text("Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. Il a été popularisé dans les années 1960 grâce à la vente de feuilles Letraset contenant des passages du Lorem Ipsum, et, plus récemment, par son inclusion dans des applications de mise en page de texte, comme Aldus PageMaker. .")])]
52−
53− // basicInfo.dataSourse =
58+ Model.getPostsForUser(with: 42) { [weak self] (posts) in
59+ self?.dataSourse = posts
60+ }
5461
5562 posts.viewController = self
5663 posts.type = .NONE
GDproject/Controller/RepeatingTimer.swiftnew file+59
@@ -0,0 +1,59 @@
1+/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
2+/// crashes that occur from calling resume multiple times on a timer that is
3+/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
4+
5+import Foundation
6+
7+class RepeatingTimer {
8+
9+ let timeInterval: TimeInterval
10+
11+ init(timeInterval: TimeInterval) {
12+ self.timeInterval = timeInterval
13+ }
14+
15+ private lazy var timer: DispatchSourceTimer = {
16+ let t = DispatchSource.makeTimerSource()
17+ t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
18+ t.setEventHandler(handler: { [weak self] in
19+ self?.eventHandler?()
20+ })
21+ return t
22+ }()
23+
24+ var eventHandler: (() -> Void)?
25+
26+ private enum State {
27+ case suspended
28+ case resumed
29+ }
30+
31+ private var state: State = .suspended
32+
33+ deinit {
34+ timer.setEventHandler {}
35+ timer.cancel()
36+ /*
37+ If the timer is suspended, calling cancel without resuming
38+ triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
39+ */
40+ resume()
41+ eventHandler = nil
42+ }
43+
44+ func resume() {
45+ if state == .resumed {
46+ return
47+ }
48+ state = .resumed
49+ timer.resume()
50+ }
51+
52+ func suspend() {
53+ if state == .suspended {
54+ return
55+ }
56+ state = .suspended
57+ timer.suspend()
58+ }
59+}
GDproject/DataStorage.swift+9−14
@@ -20,30 +20,25 @@ class DataStorage{
2020 //weak var coordinator: LogInCoordinator?
2121 static let standard = DataStorage()
2222
23− var cookie: HTTPCookie? = nil {
24− didSet {
25− (UIApplication.shared.delegate as? AppDelegate)?.tabCoordinator.start()
26− }
27− }
28− // add channel
29−
30− // delete channel
31−
3223 /**
3324 Function for setting log in status of user
3425 */
3526 func setIsLoggedIn(value: Bool){
3627 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
37− if !value {
38− (UIApplication.shared.delegate as? AppDelegate)?.logInAgain()
39− }
28+ isLoggedIn = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
4029 }
4130
4231 /**
4332 Function to determine is user logged in already or not
4433 */
45− func isLoggedIn() -> Bool {
46− return UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) && cookie != nil
34+ var isLoggedIn: Bool = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue) {
35+ didSet{
36+ if isLoggedIn {
37+ (UIApplication.shared.delegate as? AppDelegate)?.tabCoordinator.start()
38+ } else {
39+ (UIApplication.shared.delegate as? AppDelegate)?.logInAgain()
40+ }
41+ }
4742 }
4843}
4944
GDproject/Simple model/Model.swift+92−7
@@ -15,17 +15,37 @@ class Model{
1515 static var urlForDrafts = URL(string:"https://valera-denis.herokuapp.com/drafts/create")!
1616 static var urlForPublish = URL(string:"https://valera-denis.herokuapp.com/drafts/publish")!
1717 static var urlForUpdate = URL(string:"https://valera-denis.herokuapp.com/drafts/update")!
18+ static var urlForPostsForUser = URL(string:"https://valera-denis.herokuapp.com/posts/forUser")!
19+ static var urlForUsers = URL(string:"https://valera-denis.herokuapp.com/users")!
1820
1921 struct Posts: Codable {
2022 var body: [Attachments]
2123 var authorId: Int
2224 var id: Int
25+ var user: Model.Users?
2326
2427 init(body: [Attachments], authorId: Int, id: Int) {
2528 self.body = body
2629 self.authorId = authorId
2730 self.id = id
2831 }
32+
33+ init(body: [Attachments], authorId: Int, id: Int, user: Model.Users) {
34+ self.body = body
35+ self.authorId = authorId
36+ self.id = id
37+ self.user = user
38+ }
39+
40+ mutating func setUser(with: Users){
41+ self.user = with
42+ }
43+ }
44+
45+ struct Users: Codable{
46+ var secondName: String
47+ var firstName: String
48+ var id: Int
2949 }
3050
3151 struct Attachments: Codable {
@@ -36,9 +56,9 @@ class Model{
3656 }
3757 }
3858
39− static func authenticate(){
59+ static func authenticate(with id: Int, completion: @escaping ((Bool)->())) {
4060
41− let json: [String:Any] = ["authenticationId" : 9]
61+ let json: [String:Any] = ["authenticationId" : id]
4262 let jsonData = try? JSONSerialization.data(withJSONObject: json)
4363
4464 var request = URLRequest(url: url)
@@ -52,6 +72,7 @@ class Model{
5272
5373 guard let realResponse = responce.response, realResponse.statusCode == 204 else {
5474 print("Not a 204 response")
75+ completion(false)
5576 return
5677 }
5778
@@ -59,11 +80,10 @@ class Model{
5980
6081 let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
6182
83+ HTTPCookieStorage.shared.setCookie(cookies[0])
6284
63− for cookie in cookies {
64− HTTPCookieStorage.shared.setCookie(cookie)
65− DataStorage.standard.cookie = cookie
66− }
85+ DataStorage.standard.setIsLoggedIn(value: true)
86+ completion(true)
6787 }
6888 }
6989
@@ -83,6 +103,8 @@ class Model{
83103
84104 if response.response?.statusCode == 200 {
85105 print("success")
106+ } else {
107+ print(response.response?.statusCode)
86108 }
87109
88110 let decoder = JSONDecoder()
@@ -162,7 +184,6 @@ class Model{
162184 "id": \(post.id)
163185 }
164186 """
165− print(jsonUpd)
166187 var request = URLRequest(url: urlForUpdate)
167188
168189 request.httpMethod = "POST"
@@ -181,4 +202,68 @@ class Model{
181202 }
182203 }
183204 }
205+
206+
207+ static func getPostsForUser(with id: Int, completion: @escaping (([Posts])->())){
208+ let json = [
209+ "userId" : id,
210+ "limit": 10
211+ ]
212+
213+ let jsonData = try? JSONSerialization.data(withJSONObject: json)
214+
215+ var request = URLRequest(url: urlForPostsForUser)
216+ request.httpMethod = "POST"
217+
218+ // insert json data to the request
219+ request.httpBody = jsonData
220+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
221+
222+ AF.request(request).responseJSON {
223+ (response) in
224+
225+ if response.response?.statusCode == 200 {
226+ print("success")
227+ }
228+
229+ let decoder = JSONDecoder()
230+ guard let json = response.data else { return }
231+
232+ guard let newPost = try? decoder.decode([Posts].self, from: json) else { return }
233+
234+ completion(newPost)
235+ }
236+ }
237+
238+ static func getUsers(for ids: [Int], completion: @escaping (([Int:Users])->())){
239+ let json = "\(Set(ids))"
240+ print(json)
241+ var request = URLRequest(url: urlForUsers)
242+
243+ request.httpMethod = "POST"
244+
245+ // insert json data to the request
246+ request.httpBody = json.data(using: .utf8)
247+ request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
248+
249+ AF.request(request).responseJSON {
250+ (response) in
251+
252+ if response.response?.statusCode == 200 {
253+ print("successddd")
254+ }
255+
256+ let decoder = JSONDecoder()
257+ guard let json = response.data else { return }
258+
259+ guard let users = try? decoder.decode([Users].self, from: json) else { return }
260+
261+ var dict: [Int:Users] = [:]
262+ users.forEach({ (user) in
263+ dict[user.id] = user
264+ })
265+
266+ completion(dict)
267+ }
268+ }
184269}
GDproject/Supporting files/AppDelegate.swift+1−1
@@ -34,7 +34,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
3434 coordinator = LogInCoordinator(navigationController: rootController!, window: window!)
3535 tabCoordinator = TabbarCoordinator(window: window!)
3636 // TODO:- determine what to do here: log in or tabbar
37− if DataStorage.standard.isLoggedIn() {
37+ if DataStorage.standard.isLoggedIn {
3838 tabCoordinator.start()
3939 } else {
4040 coordinator.start()