Преглед изворни кода

Merge branch 'Sync' into BaseProject

Iñigo Valentin пре 9 година
родитељ
комит
eafcc57182
3 измењених фајлова са 288 додато и 10 уклоњено
  1. 55 0
      app/Base.lproj/Main.storyboard
  2. 144 1
      app/Sync.swift
  3. 89 9
      app/ViewController.swift

+ 55 - 0
app/Base.lproj/Main.storyboard

@@ -188,6 +188,7 @@
                         <segue destination="UkS-wI-Yhx" kind="show" identifier="SeguePost" id="gs2-jd-cPW"/>
                         <segue destination="k0M-Sk-DZM" kind="show" identifier="SegueAlbum" id="Puc-tv-vaD"/>
                         <segue destination="zJF-Mh-CNb" kind="show" identifier="SegueSchedule" id="A70-jo-yAP"/>
+                        <segue destination="3o1-ab-AT4" kind="show" identifier="SegueSync" id="E96-mK-gE9"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
@@ -952,9 +953,63 @@
                 <placeholder placeholderIdentifier="IBFirstResponder" id="rq0-MV-vyl" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
             <point key="canvasLocation" x="-2839" y="1801"/>
+        <!--Sync-->
+        <scene sceneID="cqg-BI-2xm">
+            <objects>
+                <viewController title="Sync" id="3o1-ab-AT4" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="RYy-ax-Nya"/>
+                        <viewControllerLayoutGuide type="bottom" id="rXD-89-8EQ"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="aKm-vb-AZR">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Logo" translatesAutoresizingMaskIntoConstraints="NO" id="dxh-Z0-luU">
+                                <rect key="frame" x="48" y="242.5" width="224" height="83"/>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sincronizando..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="up0-1V-tTd">
+                                <rect key="frame" x="0.0" y="335.5" width="320" height="30"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="30" id="mIj-oN-tq7"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="VkR-DT-y3S">
+                                <rect key="frame" x="48" y="375.5" width="224" height="2"/>
+                            </progressView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pgU-dx-MvQ">
+                                <rect key="frame" x="48" y="385.5" width="224" height="18"/>
+                                <fontDescription key="fontDescription" type="italicSystem" pointSize="15"/>
+                                <color key="textColor" red="0.31238102791878175" green="0.31238102791878175" blue="0.31238102791878175" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" red="0.70980392160000005" green="0.82745098039999998" blue="0.8980392157" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="VkR-DT-y3S" firstAttribute="top" secondItem="up0-1V-tTd" secondAttribute="bottom" constant="10" id="2jT-Qj-SnX"/>
+                            <constraint firstItem="VkR-DT-y3S" firstAttribute="width" secondItem="dxh-Z0-luU" secondAttribute="width" id="5pk-5G-ix0"/>
+                            <constraint firstItem="up0-1V-tTd" firstAttribute="centerX" secondItem="dxh-Z0-luU" secondAttribute="centerX" id="C1h-cG-8Sx"/>
+                            <constraint firstItem="pgU-dx-MvQ" firstAttribute="centerX" secondItem="VkR-DT-y3S" secondAttribute="centerX" id="QB4-zO-roz"/>
+                            <constraint firstItem="dxh-Z0-luU" firstAttribute="width" secondItem="aKm-vb-AZR" secondAttribute="width" multiplier="0.7" id="UXD-fj-qLn"/>
+                            <constraint firstItem="dxh-Z0-luU" firstAttribute="centerY" secondItem="aKm-vb-AZR" secondAttribute="centerY" id="aC5-zC-5I4"/>
+                            <constraint firstItem="up0-1V-tTd" firstAttribute="width" secondItem="aKm-vb-AZR" secondAttribute="width" id="c7R-MO-YhC"/>
+                            <constraint firstItem="pgU-dx-MvQ" firstAttribute="width" secondItem="VkR-DT-y3S" secondAttribute="width" id="dUS-Vx-M16"/>
+                            <constraint firstItem="VkR-DT-y3S" firstAttribute="centerX" secondItem="dxh-Z0-luU" secondAttribute="centerX" id="dfj-BN-WnD"/>
+                            <constraint firstItem="dxh-Z0-luU" firstAttribute="centerX" secondItem="aKm-vb-AZR" secondAttribute="centerX" id="uFb-fC-wRh"/>
+                            <constraint firstItem="up0-1V-tTd" firstAttribute="top" secondItem="dxh-Z0-luU" secondAttribute="bottom" constant="10" id="wY9-12-hcE"/>
+                            <constraint firstItem="pgU-dx-MvQ" firstAttribute="top" secondItem="VkR-DT-y3S" secondAttribute="bottom" constant="8" id="wts-Yk-LsB"/>
+                        </constraints>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="fRm-1Y-Xmm" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
         </scene>
     </scenes>
     <resources>
+        <image name="Logo" width="83" height="83"/>
         <image name="PhotoPlaceholder" width="170" height="108"/>
     </resources>
 </document>

+ 144 - 1
app/Sync.swift

@@ -27,14 +27,30 @@ Class to handle server sync.
 */
 class Sync{
 	
+	var initial: Bool = false
+	
 	/**
 	Starts the sync process.
+	Always asynchronously.
 	*/
 	init(){
 		let url = buildUrl();
 		sync(url: url)
 	}
 	
+	/**
+	Starts the sync process, synchronously or asynchronously.
+	:param: synchronous True for synchronous sync, false for asynchronously
+	*/
+	init(synchronous: Bool){
+		if synchronous == true{
+			NSLog(":SYNC:LOG: Synchronous sync started.")
+			self.initial = true
+		}
+		let url = buildUrl();
+		sync(url: url)
+	}
+	
 	/**
 	Builds the URL to perform the sync against.
 	:returns: The URL.
@@ -83,7 +99,7 @@ class Sync{
 			let dataAlbum : [String] = self.getTable(data: strData!, table: "album")
 			let dataFestival : [String] = self.getTable(data: strData!, table: "festival")
 			let dataFestivalDay : [String] = self.getTable(data: strData!, table: "festival_day")
-			//let dataFestivalEvent : [String] = self.getTable(data: strData!, table: "festival_event")
+			let dataFestivalEvent : [String] = self.getTable(data: strData!, table: "festival_event")
 			//let dataFestivalEventImage : [String] = self.getTable(data: strData!, table: "festival_event_image")
 			//let dataFestivalOffer : [String] = self.getTable(data: strData!, table: "festival_offer")
 			//let dataPeople : [String] = self.getTable(data: strData!, table: "people")
@@ -118,6 +134,7 @@ class Sync{
 			
 			self.saveTableFestival(entries: dataFestival)
 			self.saveTableFestivalDay(entries: dataFestivalDay)
+			self.saveTableFestivalEvent(entries: dataFestivalEvent)
 			
 			self.saveSettings(entries: dataSettings)
 			
@@ -141,6 +158,14 @@ class Sync{
 				NSLog(":SYNC:DEBUG: Error with request: \(error).")
 			}*/
 			//End tester
+			
+			// If it's the initial sync, hide the segue
+			if self.initial == true{
+				NSLog(":SYNC:LOG: Finishing synchronous sync.")
+				let delegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
+				delegate.controller?.initialSync(showScreen: false)
+				
+			}
 		}
 		
 		task.resume()
@@ -1554,6 +1579,124 @@ class Sync{
 		}
 	}
 	
+	/**
+	Saves the data in the table.
+	:param: entries Array of strings containing the rows of the table, in JSON format.
+	*/
+	func saveTableFestivalEvent(entries : [String]){
+		
+		NSLog(":SYNC:LOG: Saving table FestivalEvent.")
+		
+		let dateFormatter = DateFormatter()
+		dateFormatter.timeZone = TimeZone.ReferenceType.local
+		
+		//Set up context
+		let appDelegate = UIApplication.shared.delegate as! AppDelegate
+		let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
+		context.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator
+		
+		let entity =  NSEntityDescription.entity(forEntityName: "Festival_event", in: context)
+		
+		var row: NSManagedObject
+		
+		//Delete all previous entries
+		let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Festival_event")
+		let request = NSBatchDeleteRequest(fetchRequest: fetch)
+		do {
+			try context.execute(request)
+		} catch let error as NSError  {
+			NSLog(":SYNC:ERROR: Could not clean up Festival_event: \(error), \(error.userInfo).")
+		} catch {
+			NSLog(":SYNC:ERROR: Could not clean up Festival_event entity.")
+		}
+		
+		//Loop new entries
+		for entry in entries{
+			
+			row = NSManagedObject(entity: entity!, insertInto: context)
+			
+			//Get id
+			var str = entry
+			let id: Int = Int(str.subStr(start : str.indexOf(target : "\"id\":")! + 6, end : str.indexOf(target : ",\"")! - 2))!
+			row.setValue(id, forKey: "id")
+			
+			//Get gm
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let gm: Int = Int(str.subStr(start : str.indexOf(target : "\"gm\":")! + 6, end : str.indexOf(target : ",\"")! - 2))!
+			row.setValue(gm, forKey: "gm")
+			
+			//Get title_es
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let title_es : String = str.subStr(start : str.indexOf(target : "\"title_es\":")! + 12, end : str.indexOf(target : ",\"")! - 2)
+			row.setValue(title_es, forKey: "title_es")
+			
+			//Get title_en
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let title_en : String = str.subStr(start : str.indexOf(target : "\"title_en\":")! + 12, end : str.indexOf(target : ",\"")! - 2)
+			row.setValue(title_en, forKey: "title_en")
+			
+			//Get title_eu
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let title_eu : String = str.subStr(start : str.indexOf(target : "\"title_eu\":")! + 12, end : str.indexOf(target : ",\"")! - 2)
+			row.setValue(title_eu, forKey: "title_eu")
+			
+			//Get description_es
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let description_es : String = str.subStr(start : str.indexOf(target : "\"description_es\":")! + 18, end : str.indexOf(target : ",\"")! - 2)
+			if (description_es != "ul"){ //From "null"
+				row.setValue(description_es, forKey: "description_es")
+			}
+			
+			//Get description_en
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let description_en : String = str.subStr(start : str.indexOf(target : "\"description_en\":")! + 18, end : str.indexOf(target : ",\"")! - 2)
+			if (description_en != "ul"){ //From "null"
+				row.setValue(description_en, forKey: "description_en")
+			}
+			
+			//Get description_eu
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let description_eu : String = str.subStr(start : str.indexOf(target : "\"description_eu\":")! + 18, end : str.indexOf(target : ",\"")! - 2)
+			if (description_eu != "ul"){ //From "null"
+				row.setValue(description_eu, forKey: "description_eu")
+			}
+			
+			//Get host
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let host: Int = Int(str.subStr(start : str.indexOf(target : "\"host\":")! + 8, end : str.indexOf(target : ",\"")! - 2))!
+			row.setValue(host, forKey: "host")
+			
+			//Get place
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			let place: Int = Int(str.subStr(start : str.indexOf(target : "\"place\":")! + 9, end : str.indexOf(target : ",\"")! - 2))!
+			row.setValue(place, forKey: "place")
+			
+			//Get start
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
+			let start = dateFormatter.date(from: str.subStr(start : str.indexOf(target : "\"start\":")! + 9, end : str.indexOf(target : ",\"")! - 2))!
+			row.setValue(start, forKey: "start")
+			
+			//Get end
+			str = str.subStr(start : str.indexOf(target : ",\"")! + 1, end : str.length - 1)
+			dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
+			let strEnd =  str.subStr(start : str.indexOf(target : "\"end\":")! + 7, end : str.length - 2)
+			if (strEnd != "ul"){ //From "null"
+				let end = dateFormatter.date(from: strEnd)!
+				row.setValue(end, forKey: "end")
+			}
+			
+			//Save CoreData
+			do {
+				try context.save()
+			} catch let error as NSError  {
+				NSLog(":SYNC:ERROR: Could not store Festival_event entry \(error.userInfo).")
+			} catch {
+				NSLog(":SYNC:ERROR: Could not store Festival_event entry.")
+			}
+		}
+	}
+	
 	/**
 	Saves the received settings.
 	:param: entries Array of strings containing the rows of the table, in JSON format.

+ 89 - 9
app/ViewController.swift

@@ -30,6 +30,9 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection
 	
 	//The menu collection.
 	var sectionCollection: UICollectionView!
+	
+	// Need to save the sync segue.
+	var syncSegue: UIStoryboardSegue?
 
 	//Each of the sections of the app.
 	@IBOutlet var containerViewGallery: GalleryView!
@@ -41,22 +44,40 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection
 	
 	var passId: Int = -1
 	
+	/**
+	 Controller initializer.
+	*/
+	required init?(coder aDecoder: NSCoder) {
+		super.init(coder: aDecoder)
+	}
+	
+	/**
+	 Retrieves the application context.
+	 :return: The application context.
+	*/
 	func getContext () -> NSManagedObjectContext {
 		//let appDelegate = UIApplication.shared.delegate as! AppDelegate
 		//return appDelegate.persistentContainer.viewContext
 		return NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
 	}
 	
+	/**
+	 Shows a post by loading the controller.
+	 :param: id The post id.
+	*/
 	func showPost(id: Int){
-		print("CONTROLLER:DEBUG: Showing Post \(id)")
+		NSLog(":CONTROLLER:DEBUG: Showing Post \(id)")
 		self.passId = id
 		performSegue(withIdentifier: "SeguePost", sender: nil)
 	}
 	
+	/**
+	 Shows a post by loading the controller.
+	 :param: id The album id.
+	*/
 	func showAlbum(id: Int){
 		NSLog(":CONTROLLER:DEBUG: Showing album \(id)")
 		self.passId = id
-		// TODO: Uncomment when ready
 		performSegue(withIdentifier: "SegueAlbum", sender: nil)
 	}
 	
@@ -72,6 +93,29 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection
 		performSegue(withIdentifier: "SegueSchedule", sender: nil)
 	}
 	
+	/**
+	 Handles the initial sync process.
+	 It can start it, showing the sync screen, or finish it, hidding the screen.
+	 :param: showScreen True to start the sync, false to end it.
+	*/
+	func initialSync(showScreen: Bool){
+		if showScreen == true{
+			NSLog(":CONTROLLER:DEBUG: Showing initial sync screen.")
+			performSegue(withIdentifier: "SegueSync", sender: nil)
+			Sync(synchronous: true)
+		}
+		else{
+			NSLog(":CONTROLLER:DEBUG: Hidding initial sync screen.")
+			syncSegue?.destination.dismiss(animated: true, completion: nil)
+		}
+	}
+	
+	/**
+	 Run before performing a segue.
+	 Assigns id if neccessary.
+	 :param: segue The segue to perform.
+	 :sender: The calling view.
+	*/
 	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
 		NSLog(":CONTROLLER:DEBUG: preparing for segue '\(segue.identifier)' with id \(self.passId)")
 		if segue.identifier == "SeguePost"{
@@ -87,6 +131,8 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection
 			else{
 				(segue.destination as! ScheduleViewController).margolari = false
 			}
+		if segue.identifier == "SegueSync"{
+			self.syncSegue = segue
 		}
 	}
 	
@@ -101,23 +147,57 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection
 		self.containerViewActivities.alpha = 0
 		self.containerViewBlog.alpha = 0
 		self.containerViewGallery.alpha = 0
-		
-		
-		//self.containerViewBlog.isHidden = true
 				
-		print("CONTROLLER:LOG: viewDidLoad()")
-		print("CONTROLLER:DEBUG: Skyp sync")
+		NSLog(":CONTROLLER:LOG: viewDidLoad()")
+		NSLog(":CONTROLLER:DEBUG: Skyp sync")
 		//Sync()
 		
 		super.viewDidLoad()
-		// Do any additional setup after loading the view, typically from a nib.
 
-		
 		//self.containerViewBlog.setController(controller: self as ViewController)
 		
 		delegate = UIApplication.shared.delegate as! AppDelegate
 		delegate?.controller = self
 		
+		
+	}
+	
+	/**
+	 Executed when the view is actually shown.
+	 Performs the initial sync in the first run.
+	 It also generates a user id if none exists.
+	 :param: animated Wether the controller appearance must be animated or not.
+	*/
+	override func viewDidAppear(_ animated: Bool) {
+		super.viewDidAppear(animated)
+		if UserDefaults.standard.object(forKey: "userId") == nil{
+			let newUid = randomString(length: 16)
+			let defaults = UserDefaults.standard
+			defaults.set(newUid, forKey: "userId")
+			initialSync(showScreen: true)
+		}
+		
+	}
+	
+	/**
+	 Generates a random string to be used as a user identifier.
+	 :param: length The length of the generated string.
+	 :return: A random alphanumeric string with the indicated length.
+	*/
+	func randomString(length: Int) -> String {
+		
+		let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+		let len = UInt32(letters.length)
+		
+		var randomString = ""
+		
+		for _ in 0 ..< length {
+			let rand = arc4random_uniform(len)
+			var nextChar = letters.character(at: Int(rand))
+			randomString += NSString(characters: &nextChar, length: 1) as String
+		}
+		
+		return randomString
 	}
 
 	/**