git.iko.soy/dashared/GDproject
on master help sign in
improved log in (now with numer). user-experience is improved
fce9ad4Darya Rednikina
GDproject.xcodeproj/project.pbxproj+4
@@ -12,6 +12,7 @@
12 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
12 124CC7032221C00C009DF531 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124CC7022221C00C009DF531 /* Model.swift */; };
13 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; };
13 124CC7052221C2BB009DF531 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; };
14 124CC7072221C2C3009DF531 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 124CC7042221C2BA009DF531 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
14 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 */; };
15 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
16 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57C22171D2A008A3575 /* ProfileViewController.swift */; };
16 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
17 125BD57F22171D73008A3575 /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD57E22171D73008A3575 /* Extentions.swift */; };
17 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
18 125BD5812217314A008A3575 /* NewsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BD5802217314A008A3575 /* NewsVC.swift */; };
@@ -75,6 +76,7 @@
75 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
76 123E37A6221F1DD700F6E42A /* MainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
76 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
77 124CC7022221C00C009DF531 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
77 124CC7042221C2BA009DF531 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/iOS/Alamofire.framework; sourceTree = "<group>"; };
78 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>"; };
78 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
80 125BD57C22171D2A008A3575 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
79 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
81 125BD57E22171D73008A3575 /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
80 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
82 125BD5802217314A008A3575 /* NewsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsVC.swift; sourceTree = "<group>"; };
@@ -222,6 +224,7 @@
222 12E36DCF22145923006FCDD7 /* Controller */ = {
224 12E36DCF22145923006FCDD7 /* Controller */ = {
223 isa = PBXGroup;
225 isa = PBXGroup;
224 children = (
226 children = (
227+ 1253867C2223DC9F00CC5EA7 /* RepeatingTimer.swift */,
225 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
228 1291BE32222154C3009D3F23 /* Coordinator tab bar? */,
226 1291BE3722218DC4009D3F23 /* Log In */,
229 1291BE3722218DC4009D3F23 /* Log In */,
227 1288B5CC221F0EFE002BE6B1 /* Profile */,
230 1288B5CC221F0EFE002BE6B1 /* Profile */,
@@ -343,6 +346,7 @@
343 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
346 12E36DD122148122006FCDD7 /* FullPostController.swift in Sources */,
344 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
347 1288B5CE221F1158002BE6B1 /* DataStorage.swift in Sources */,
345 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
348 125BD57D22171D2A008A3575 /* ProfileViewController.swift in Sources */,
349+ 1253867D2223DC9F00CC5EA7 /* RepeatingTimer.swift in Sources */,
346 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
350 12DB7FDB2218590C0096878E /* InfoCell.swift in Sources */,
347 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
351 12DB7FD922181E660096878E /* BasicInfoCell.swift in Sources */,
348 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
352 12D7D135221C42B700B35452 /* ChannelController.swift in Sources */,
GDproject/Base.lproj/Main.storyboard+7−1
@@ -387,16 +387,22 @@
387 </textField>
387 </textField>
388 </subviews>
388 </subviews>
389 </stackView>
389 </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>
390 </subviews>
393 </subviews>
391 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
394 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
392 <constraints>
395 <constraints>
393 <constraint firstItem="UwC-m5-jA5" firstAttribute="leading" secondItem="LOR-Yw-0FU" secondAttribute="leading" constant="16" id="M6z-nU-H40"/>
396 <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"/>
394 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
399 <constraint firstItem="LOR-Yw-0FU" firstAttribute="trailing" secondItem="UwC-m5-jA5" secondAttribute="trailing" constant="16" id="jST-rr-MMX"/>
395 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
400 <constraint firstItem="UwC-m5-jA5" firstAttribute="top" secondItem="LOR-Yw-0FU" secondAttribute="top" constant="80" id="rvy-Ii-bwS"/>
396 </constraints>
401 </constraints>
397 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
402 <viewLayoutGuide key="safeArea" id="LOR-Yw-0FU"/>
398 </view>
403 </view>
399 <connections>
404 <connections>
405+ <outlet property="indicatorView" destination="K3b-gv-JPp" id="mSR-QU-VZQ"/>
400 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
406 <outlet property="mailTextField" destination="mFP-WG-jY5" id="7SN-qe-DDm"/>
401 </connections>
407 </connections>
402 </viewController>
408 </viewController>
@@ -437,7 +443,7 @@
437 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
443 <outlet property="delegate" destination="Nqc-TI-ueQ" id="kTM-Be-8qb"/>
438 </connections>
444 </connections>
439 </tableView>
445 </tableView>
440− <navigationItem key="navigationItem" id="b8E-6f-hLM"/>
446+ <navigationItem key="navigationItem" largeTitleDisplayMode="always" id="b8E-6f-hLM"/>
441 </tableViewController>
447 </tableViewController>
442 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
448 <placeholder placeholderIdentifier="IBFirstResponder" id="c9d-Kh-itI" userLabel="First Responder" sceneMemberID="firstResponder"/>
443 </objects>
449 </objects>
GDproject/Controller/ News and channels/NewPostViewController.swift+12
@@ -49,11 +49,22 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
49 Model.createDraft { [weak self] (int) in
49 Model.createDraft { [weak self] (int) in
50 self?.indexOfPost = int
50 self?.indexOfPost = int
51 }
51 }
52+ //createTimerForUpdates()
53+
52 setUpMD()
54 setUpMD()
53 setUpTextView()
55 setUpTextView()
54 setUpAccessoryView()
56 setUpAccessoryView()
55 }
57 }
56
58
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+
57 override func viewWillAppear(_ animated: Bool) {
68 override func viewWillAppear(_ animated: Bool) {
58 super.viewWillAppear(animated)
69 super.viewWillAppear(animated)
59
70
@@ -181,6 +192,7 @@ class NewPostViewController: UIViewController, UITextViewDelegate {
181 let layoutManager = NSLayoutManager()
192 let layoutManager = NSLayoutManager()
182
193
183 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
194 // Assign the `UITextView`'s `NSLayoutManager` to the `NSTextStorage` subclass
195+ //textStorage.addLayoutManager(textView.layoutManager)
184 textStorage.addLayoutManager(layoutManager)
196 textStorage.addLayoutManager(layoutManager)
185
197
186 let textContainer = NSTextContainer()
198 let textContainer = NSTextContainer()
GDproject/Controller/ News and channels/NewsController.swift+55−12
@@ -25,11 +25,32 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
25 self.channel = channel
25 self.channel = channel
26 }
26 }
27
27
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+
28 var channel: Channel?{
43 var channel: Channel?{
29 didSet{
44 didSet{
30− news.dataSourse = channel?.posts ?? []
45+ let hh = channel?.posts.map({ (post) -> Int in
46+ post.authorId
47+ })
48+
31 navigationItem.title = channel?.title ?? ""
49 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+ }
33 }
54 }
34 }
55 }
35
56
@@ -44,22 +65,29 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
44
65
45 var news = NewsVC()
66 var news = NewsVC()
46
67
68+ var refreshContr: UIRefreshControl? = {
69+ let rc = UIRefreshControl()
70+ rc.addTarget(self, action: #selector(refreshPostsData(_:)), for: .valueChanged)
71+ return rc
72+ }()
73+
47 override func viewDidLoad()
74 override func viewDidLoad()
48 {
75 {
49 super.viewDidLoad()
76 super.viewDidLoad()
77+ navigationItem.title = "Loading ..."
78+ tableView.refreshControl = refreshContr
79+ // Configure Refresh Control
50
80
81+ Model.getLast { [weak self] (channel) in
82+ self?.channel = channel
83+ }
51
84
52 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
85 tableView.register(PostViewCell.self, forCellReuseIdentifier: postCellId)
53 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
86 tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
54
87
55− searchController.delegate = self
88+ setUpSearchContr()
56− searchController.obscuresBackgroundDuringPresentation = false
57− navigationItem.searchController = searchController
58− definesPresentationContext = true
59− searchController.searchBar.placeholder = "Search anything"
60
89
61 news.viewController = self
90 news.viewController = self
62−
63 news.type = .NONE
91 news.type = .NONE
64
92
65 setUpNavigationItemsforPosts()
93 setUpNavigationItemsforPosts()
@@ -67,6 +95,23 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
67 //setUpBanner()
95 //setUpBanner()
68 }
96 }
69
97
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+
70 deinit {
115 deinit {
71 print("news clear")
116 print("news clear")
72 }
117 }
@@ -86,11 +131,9 @@ class NewsController: UITableViewController, UISearchControllerDelegate, NewPost
86 searchController.isActive = false
131 searchController.isActive = false
87 // TODO:- display something if no posts are availiable
132 // TODO:- display something if no posts are availiable
88
133
89− Model.getLast { (channel) in
134+ refreshContr?.beginRefreshing()
90− self.channel = channel
91− }
92
135
93− tableView.reloadData()
136+ // tableView.reloadData()
94 }
137 }
95
138
96 func setUpNavigationItemsforPosts(){
139 func setUpNavigationItemsforPosts(){
GDproject/Controller/ News and channels/PostViewCell.swift+4
@@ -83,8 +83,12 @@ class PostViewCell: UITableViewCell
83 {
83 {
84 views.append(createTextView(with: attachment.markdown))
84 views.append(createTextView(with: attachment.markdown))
85 }
85 }
86+
87+ nameLabel.text = "\(post.user?.firstName ?? "") \(post.user?.secondName ?? "")"
88+ fullNameLabel.text = "\(post.authorId)"
86 setUpInStackView(isFullVersoin)
89 setUpInStackView(isFullVersoin)
87 }
90 }
91+
88 let hashtags = ["Kek", "Lol"]
92 let hashtags = ["Kek", "Lol"]
89 func setUpInStackView(_ full : Bool){
93 func setUpInStackView(_ full : Bool){
90 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
94 let nameStackView = UIStackView(arrangedSubviews: [nameLabel, fullNameLabel])
GDproject/Controller/Log In/LogInCoordinator.swift+6−4
@@ -30,11 +30,13 @@ class LogInCoordinator{
30
30
31 private func showLogInPage(){
31 private func showLogInPage(){
32 let controller = UIStoryboard.makeLogIn()
32 let controller = UIStoryboard.makeLogIn()
33− controller.onLogIn = { [weak self] in
33+ controller.onLogIn = {
34− DataStorage.standard.setIsLoggedIn(value: true)
34+ (id) in
35− Model.authenticate()
35+ Model.authenticate(with: id) {
36+ (res) in controller.authenticateSucceeded = res
37+ }
36 }
38 }
37− //navigationController?.setViewControllers([controller], animated: false)
39+
38 navigationController?.pushViewController(controller, animated: false)
40 navigationController?.pushViewController(controller, animated: false)
39 }
41 }
40
42
GDproject/Controller/Log In/LogInViewController.swift+21−3
@@ -12,8 +12,18 @@ class LogInViewController: UIViewController {
12
12
13 @IBOutlet weak var mailTextField: UITextField!
13 @IBOutlet weak var mailTextField: UITextField!
14
14
15− var onLogIn: (()->())?
15+ @IBOutlet weak var indicatorView: UIActivityIndicatorView!
16
16
17+ var onLogIn: ((Int)->())?
18+
19+ var authenticateSucceeded: Bool? {
20+ didSet {
21+ if !authenticateSucceeded! {
22+ indicatorView.stopAnimating()
23+ indicatorView.isHidden = true
24+ }
25+ }
26+ }
17 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
27 static let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 0.5)
18
28
19 var bottomConstraint: NSLayoutConstraint?
29 var bottomConstraint: NSLayoutConstraint?
@@ -64,12 +74,20 @@ class LogInViewController: UIViewController {
64 @objc func activateLogInProcess(){
74 @objc func activateLogInProcess(){
65 if logInButton.isEnabled {
75 if logInButton.isEnabled {
66 // MARK:- when log in is succeeded do I need to go there?
76 // MARK:- when log in is succeeded do I need to go there?
77+ if let id = Int(mailTextField.text!){
78+ indicatorView.isHidden = false
79+ indicatorView.startAnimating()
67− onLogIn?()
80+ onLogIn?(id)
68− view.endEditing(true)
81+ view.endEditing(true)
82+ } else {
83+ logInButton.isEnabled = false
84+ logInButton.setTitleColor(LogInViewController.titleColor.withAlphaComponent(0.5), for: .normal)
85+ }
69 }
86 }
70 }
87 }
71
88
72 func setUpView(){
89 func setUpView(){
90+ indicatorView.isHidden = true
73 mailTextField.delegate = self
91 mailTextField.delegate = self
74 view.addSubview(keyboardBar)
92 view.addSubview(keyboardBar)
75
93
GDproject/Controller/Profile/ProfileViewController.swift+10−3
@@ -44,13 +44,20 @@ class ProfileViewController: UIViewController
44 var basicInfo = BasicInfoController()
44 var basicInfo = BasicInfoController()
45 var posts = NewsVC()
45 var posts = NewsVC()
46
46
47+ var dataSourse: [Model.Posts]?{
48+ didSet{
49+ self.posts.dataSourse = dataSourse!
50+ tableView.reloadData()
51+ }
52+ }
53+
47 override func viewDidLoad()
54 override func viewDidLoad()
48 {
55 {
49 super.viewDidLoad()
56 super.viewDidLoad()
50
57
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. .")])]
58+ Model.getPostsForUser(with: 42) { [weak self] (posts) in
52−
59+ self?.dataSourse = posts
53− // basicInfo.dataSourse =
60+ }
54
61
55 posts.viewController = self
62 posts.viewController = self
56 posts.type = .NONE
63 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{
20 //weak var coordinator: LogInCoordinator?
20 //weak var coordinator: LogInCoordinator?
21 static let standard = DataStorage()
21 static let standard = DataStorage()
22
22
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−
32 /**
23 /**
33 Function for setting log in status of user
24 Function for setting log in status of user
34 */
25 */
35 func setIsLoggedIn(value: Bool){
26 func setIsLoggedIn(value: Bool){
36 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
27 UserDefaults.standard.set(value, forKey: UserDefaultsKeys.loggedIn.rawValue)
37− if !value {
28+ isLoggedIn = UserDefaults.standard.bool(forKey: UserDefaultsKeys.loggedIn.rawValue)
38− (UIApplication.shared.delegate as? AppDelegate)?.logInAgain()
39− }
40 }
29 }
41
30
42 /**
31 /**
43 Function to determine is user logged in already or not
32 Function to determine is user logged in already or not
44 */
33 */
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+ }
47 }
42 }
48}
43}
49
44
GDproject/Simple model/Model.swift+92−7
@@ -15,17 +15,37 @@ class Model{
15 static var urlForDrafts = URL(string:"https://valera-denis.herokuapp.com/drafts/create")!
15 static var urlForDrafts = URL(string:"https://valera-denis.herokuapp.com/drafts/create")!
16 static var urlForPublish = URL(string:"https://valera-denis.herokuapp.com/drafts/publish")!
16 static var urlForPublish = URL(string:"https://valera-denis.herokuapp.com/drafts/publish")!
17 static var urlForUpdate = URL(string:"https://valera-denis.herokuapp.com/drafts/update")!
17 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")!
18
20
19 struct Posts: Codable {
21 struct Posts: Codable {
20 var body: [Attachments]
22 var body: [Attachments]
21 var authorId: Int
23 var authorId: Int
22 var id: Int
24 var id: Int
25+ var user: Model.Users?
23
26
24 init(body: [Attachments], authorId: Int, id: Int) {
27 init(body: [Attachments], authorId: Int, id: Int) {
25 self.body = body
28 self.body = body
26 self.authorId = authorId
29 self.authorId = authorId
27 self.id = id
30 self.id = id
28 }
31 }
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
29 }
49 }
30
50
31 struct Attachments: Codable {
51 struct Attachments: Codable {
@@ -36,9 +56,9 @@ class Model{
36 }
56 }
37 }
57 }
38
58
39− static func authenticate(){
59+ static func authenticate(with id: Int, completion: @escaping ((Bool)->())) {
40
60
41− let json: [String:Any] = ["authenticationId" : 9]
61+ let json: [String:Any] = ["authenticationId" : id]
42 let jsonData = try? JSONSerialization.data(withJSONObject: json)
62 let jsonData = try? JSONSerialization.data(withJSONObject: json)
43
63
44 var request = URLRequest(url: url)
64 var request = URLRequest(url: url)
@@ -52,6 +72,7 @@ class Model{
52
72
53 guard let realResponse = responce.response, realResponse.statusCode == 204 else {
73 guard let realResponse = responce.response, realResponse.statusCode == 204 else {
54 print("Not a 204 response")
74 print("Not a 204 response")
75+ completion(false)
55 return
76 return
56 }
77 }
57
78
@@ -59,11 +80,10 @@ class Model{
59
80
60 let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
81 let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields!, for: realResponse.url!)
61
82
83+ HTTPCookieStorage.shared.setCookie(cookies[0])
62
84
63− for cookie in cookies {
64− HTTPCookieStorage.shared.setCookie(cookie)
65− DataStorage.standard.cookie = cookie
85+ DataStorage.standard.setIsLoggedIn(value: true)
66− }
86+ completion(true)
67 }
87 }
68 }
88 }
69
89
@@ -83,6 +103,8 @@ class Model{
83
103
84 if response.response?.statusCode == 200 {
104 if response.response?.statusCode == 200 {
85 print("success")
105 print("success")
106+ } else {
107+ print(response.response?.statusCode)
86 }
108 }
87
109
88 let decoder = JSONDecoder()
110 let decoder = JSONDecoder()
@@ -162,7 +184,6 @@ class Model{
162 "id": \(post.id)
184 "id": \(post.id)
163 }
185 }
164 """
186 """
165− print(jsonUpd)
166 var request = URLRequest(url: urlForUpdate)
187 var request = URLRequest(url: urlForUpdate)
167
188
168 request.httpMethod = "POST"
189 request.httpMethod = "POST"
@@ -181,4 +202,68 @@ class Model{
181 }
202 }
182 }
203 }
183 }
204 }
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+ }
184}
269}
GDproject/Supporting files/AppDelegate.swift+1−1
@@ -34,7 +34,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
34 coordinator = LogInCoordinator(navigationController: rootController!, window: window!)
34 coordinator = LogInCoordinator(navigationController: rootController!, window: window!)
35 tabCoordinator = TabbarCoordinator(window: window!)
35 tabCoordinator = TabbarCoordinator(window: window!)
36 // TODO:- determine what to do here: log in or tabbar
36 // TODO:- determine what to do here: log in or tabbar
37− if DataStorage.standard.isLoggedIn() {
37+ if DataStorage.standard.isLoggedIn {
38 tabCoordinator.start()
38 tabCoordinator.start()
39 } else {
39 } else {
40 coordinator.start()
40 coordinator.start()