0% found this document useful (0 votes)
473 views110 pages

Iphone App Develpment

The document discusses Xcode and the Swift programming language used for iOS development. It provides an overview of Xcode as the IDE for building iOS apps and describes Swift as the main programming language. It also discusses the iOS SDK and tools for writing iOS apps in Swift and Objective-C.

Uploaded by

programmer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
473 views110 pages

Iphone App Develpment

The document discusses Xcode and the Swift programming language used for iOS development. It provides an overview of Xcode as the IDE for building iOS apps and describes Swift as the main programming language. It also discusses the iOS SDK and tools for writing iOS apps in Swift and Objective-C.

Uploaded by

programmer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 110

Ce373:Mobile Application Development 16CE068

IOS PRACTICALS
PRACTICAL 10
10)Aim: Introduction to Xcode, Swift, and the iOS SDK and App
Design
Xcode is used to build apps for iPhone and iPad, as well as apps for Mac, Apple Watch and Apple TV. These
tutorials are based on Xcode 8.1, released on October 27, 2016. Xcode 8.1 is the latest version of Apple’s
integrated development environment (IDE) and is completely free. If you want to code along with the tutorials,
you can download Xcode from https://developer.apple.com/download/or directly from the Mac App Store. You can
use Xcode not only to build apps for iPhone and iPads, but you can use it to build apps for Mac, Apple Watch and
Apple TV as well.

Swift is a general-purpose, multi-paradigm, compiled programming language developed by Apple


Inc. for iOS, macOS, watchOS, tvOS, and Linux. Swift is designed to work with Apple's Cocoa and Cocoa
Touch frameworks and the large body of existing Objective-C code written for Apple products. It is built with the
open source LLVM compiler framework and has been included in Xcode since version 6. On platforms other than
Linux,[10] it uses the Objective-C runtime library which allows C, Objective-C, C++ and Swift code to run within
one program.
Apple intended Swift to support many core concepts associated with Objective-C, notably dynamic dispatch,
widespread late binding, extensible programming and similar features, but in a "safer" way, making it easier to
catch software bugs; Swift has features addressing some common programming errors like null
pointer dereferencing and provides syntactic sugar to help avoid the pyramid of doom. Swift supports the concept
of protocol extensibility, an extensibility system that can be applied to types, structs and classes, which Apple
promotes as a real change in programming paradigms they term "protocol-oriented programming" [12] (similar
to traits).[13]
Swift was introduced at Apple's 2014 Worldwide Developers Conference (WWDC). It underwent an upgrade to
version 1.2 during 2014 and a more major upgrade to Swift 2 at WWDC 2015. Initially a proprietary language,
version 2.2 was made open-source software under the Apache License 2.0 on December 3, 2015, for Apple's
platforms and Linux.[15][16]
In March 2017, Swift made the top 10 in the monthly TIOBE index ranking of popular programming languages,
[17]
and was ranked 11th at the end of 2017. [18] By October 2017, however, Swift had begun to lose momentum in
the TIOBE index as mobile development moved toward Xamarin and C#, as well as similar tools for JavaScript.
[19]
As of April 2018, Swift ranked No. 15 at 1.53% share, losing 0.75% from its 2.28% share just one year earlier.
[20]

Different major versions have been released at an annual schedule with incompatible syntax and library
invocations each, requiring significant source code rewrites. For larger code bases this has caused many
developers to dismiss Swift until a more stable version becomes available.

The iOS SDK (Software Development Kit) (formerly iPhone SDK) is a software development kit developed
by Apple Inc. The kit allows for the development of mobile apps on Apple's iOS operating system.
While originally developing iPhone prior to its unveiling in 2007, Apple's then-CEO Steve Jobs did not intend to let
third-party developers build native apps for iOS, instead directing them to make web applications for the Safari
web browser. However, backlash from developers prompted the company to reconsider, with Jobs announcing in
October 2007 that Apple would have a software development kit available for developers by February 2008. The
SDK was released on March 6, 2008.
The SDK is a free download for users of Mac personal computers. It is not available for Microsoft Windows PCs.
The SDK contains sets giving developers access to various functions and services of iOS devices, such as
hardware and software attributes. It also contains an iPhone simulatorto mimic the look and feel of the device on
the computer while developing. New versions of the SDK accompany new versions of iOS. In order to test
applications, get technical support, and distribute apps through App Store, developers are required to subscribe
to the Apple Developer Program.
Combined with Xcode, the iOS SDK helps developers write iOS apps using officially supported programming
languages, including Swift and Objective-C. Other companies have also created tools that allow for the
development of native iOS apps using their respective programming languages.

CSPIT 97
Ce373:Mobile Application Development 16CE068

PRACTICAL 11
11) Aim:GoodAsOldPhones I& LoveTweet mplementation using
swift
 GoodAsOldPhone

ProductViewController.shift
import UIKit

class ProductViewController: UIViewController {

@IBOutlet var productImageView: UIImageView!


@IBOutlet var productNameLabel: UILabel!

var product: Product?

override func viewDidLoad() {


super.viewDidLoad()

productNameLabel.text = product?.name

if let imageName = product?.fullscreenImageName {


productImageView.image = UIImage(named: imageName)
}
}

@IBAction func addToCartButtonDidTap(_ sender: AnyObject) {


print("Add to cart successfully")
}
}

ContactViewController.shift
import UIKit

class ContactViewController: UIViewController {

@IBOutlet weak var scrollView: UIScrollView!

override func viewDidLoad() {


super.viewDidLoad()
view.addSubview(scrollView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

if #available(iOS 11.0, *) {
scrollView.frame = CGRect(x: 0, y: view.safeAreaInsets.top, width: view.frame.width, height:
view.frame.height - view.safeAreaInsets.bottom - view.safeAreaInsets.top)
} else {
scrollView.frame = CGRect(x: 0, y: topLayoutGuide.length, width: view.frame.width, height:
view.frame.height - topLayoutGuide.length - bottomLayoutGuide.length)
}

scrollView.contentSize = CGSize(width: self.view.frame.width, height: 800)


}
}

CSPIT 98
Ce373:Mobile Application Development 16CE068

ProductsTableViewController.swift
import UIKit

class ProductsTableViewController: UITableViewController {


fileprivate var products: [Product]?
fileprivate let identifer = "productCell"

override func viewDidLoad() {


super.viewDidLoad()

products = [
Product(name: "1907 Wall Set", cellImageName: "image-cell1", fullscreenImageName: "phone-
fullscreen1"),
Product(name: "1921 Dial Phone", cellImageName: "image-cell2", fullscreenImageName: "phone-
fullscreen2"),
Product(name: "1937 Desk Set", cellImageName: "image-cell3", fullscreenImageName: "phone-
fullscreen3"),
Product(name: "1984 Moto Portable", cellImageName: "image-cell4", fullscreenImageName:
"phone-fullscreen4")
]
}

// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products?.count ?? 0
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->


UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifer, for: indexPath)
guard let products = products else { return cell }

cell.textLabel?.text = products[(indexPath as NSIndexPath).row].name

if let imageName = products[(indexPath as NSIndexPath).row].cellImageName {


cell.imageView?.image = UIImage(named: imageName)
}
return cell;
}
// MARK: - View Transfer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showProduct" {
if let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell),
let productVC = segue.destination as? ProductViewController {
productVC.product = products?[(indexPath as NSIndexPath).row]
}
}
}
}
Product.swift
import Foundation
class Product {
var name: String?
var cellImageName: String?
var fullscreenImageName: String?

CSPIT 99
Ce373:Mobile Application Development 16CE068

init(name: String, cellImageName: String, fullscreenImageName: String) {


self.name = name
self.cellImageName = cellImageName
self.fullscreenImageName = fullscreenImageName
}
}
Output:

CSPIT 100
Ce373:Mobile Application Development 16CE068

 Lovetweet
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CAAnimationDelegate {

var window: UIWindow?


var mask: CALayer?
var imageView: UIImageView?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)

if let window = window {


// add background imageView
imageView = UIImageView(frame: window.frame)
imageView!.image = UIImage(named: "twitterScreen")
window.addSubview(imageView!)

// set up mask
mask = CALayer()
mask?.contents = UIImage(named: "twitterBird")?.cgImage
mask?.position = window.center
mask?.bounds = CGRect(x: 0, y: 0, width: 100, height: 80)
imageView!.layer.mask = mask

animateMask()

CSPIT 101
Ce373:Mobile Application Development 16CE068

// make window visible


window.rootViewController = UIViewController()
window.backgroundColor = UIColor(red: 70/255, green: 154/255, blue: 233/255, alpha: 1)
window.makeKeyAndVisible()
}
UIApplication.shared.isStatusBarHidden = true
return true
}
func animateMask() {
// init key frame animation
let keyFrameAnimation = CAKeyframeAnimation(keyPath: "bounds")
keyFrameAnimation.delegate = self
keyFrameAnimation.duration = 1
keyFrameAnimation.beginTime = CACurrentMediaTime() + 1

let initalBounds = NSValue(cgRect: mask!.bounds)


let secondBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 80, height: 64))
let finalBounds = NSValue(cgRect: CGRect(x: 0, y: 0, width: 2000, height: 2000))
keyFrameAnimation.values = [initalBounds, secondBounds, finalBounds]

// set up time interals


keyFrameAnimation.keyTimes = [0, 0.3, 1]

// add animation to current view


keyFrameAnimation.timingFunctions = [CAMediaTimingFunction(name:
kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name:
kCAMediaTimingFunctionEaseOut)]
mask!.add(keyFrameAnimation, forKey: "bounds")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
imageView?.layer.mask = nil
}
}
Output:

CSPIT 102
Ce373:Mobile Application Development 16CE068

PRACTICAL 12
12)Aim:Implementation of Stopwatch, To Do List & CandySearch in
IoS

 Stopwatch
ViewController.swift
import UIKit

class ViewController: UIViewController, UITableViewDelegate {


// MARK: - Variables
fileprivate let mainStopwatch: Stopwatch = Stopwatch()
fileprivate let lapStopwatch: Stopwatch = Stopwatch()
fileprivate var isPlay: Bool = false
fileprivate var laps: [String] = []

// MARK: - UI components
@IBOutlet weak var timerLabel: UILabel!
@IBOutlet weak var lapTimerLabel: UILabel!
@IBOutlet weak var playPauseButton: UIButton!
@IBOutlet weak var lapRestButton: UIButton!
@IBOutlet weak var lapsTableView: UITableView!

// MARK: - Life Cycle


override func viewDidLoad() {
super.viewDidLoad()

let initCircleButton: (UIButton) -> Void = { button in


button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.backgroundColor = UIColor.white
}

initCircleButton(playPauseButton)
initCircleButton(lapRestButton)

lapRestButton.isEnabled = false

lapsTableView.delegate = self;
lapsTableView.dataSource = self;
}

// MARK: - UI Settings
override var shouldAutorotate : Bool {
return false
}

override var preferredStatusBarStyle : UIStatusBarStyle {


return UIStatusBarStyle.lightContent
}

override var supportedInterfaceOrientations : UIInterfaceOrientationMask {


return UIInterfaceOrientationMask.portrait
}

// MARK: - Actions
@IBAction func playPauseTimer(_ sender: AnyObject) {

CSPIT 103
Ce373:Mobile Application Development 16CE068

lapRestButton.isEnabled = true

changeButton(lapRestButton, title: "Lap", titleColor: UIColor.black)

if !isPlay {
unowned let weakSelf = self

mainStopwatch.timer = Timer.scheduledTimer(timeInterval: 0.035, target: weakSelf, selector:


Selector.updateMainTimer, userInfo: nil, repeats: true)
lapStopwatch.timer = Timer.scheduledTimer(timeInterval: 0.035, target: weakSelf, selector:
Selector.updateLapTimer, userInfo: nil, repeats: true)

RunLoop.current.add(mainStopwatch.timer, forMode: .commonModes)


RunLoop.current.add(lapStopwatch.timer, forMode: .commonModes)

isPlay = true
changeButton(playPauseButton, title: "Stop", titleColor: UIColor.red)
} else {

mainStopwatch.timer.invalidate()
lapStopwatch.timer.invalidate()
isPlay = false
changeButton(playPauseButton, title: "Start", titleColor: UIColor.green)
changeButton(lapRestButton, title: "Reset", titleColor: UIColor.black)
}
}

@IBAction func lapResetTimer(_ sender: AnyObject) {


if !isPlay {
resetMainTimer()
resetLapTimer()
changeButton(lapRestButton, title: "Lap", titleColor: UIColor.lightGray)
lapRestButton.isEnabled = false
} else {
if let timerLabelText = timerLabel.text {
laps.append(timerLabelText)
}
lapsTableView.reloadData()
resetLapTimer()
unowned let weakSelf = self
lapStopwatch.timer = Timer.scheduledTimer(timeInterval: 0.035, target: weakSelf, selector:
Selector.updateLapTimer, userInfo: nil, repeats: true)
RunLoop.current.add(lapStopwatch.timer, forMode: .commonModes)
}
}

// MARK: - Private Helpers


fileprivate func changeButton(_ button: UIButton, title: String, titleColor: UIColor) {
button.setTitle(title, for: UIControlState())
button.setTitleColor(titleColor, for: UIControlState())
}

fileprivate func resetMainTimer() {


resetTimer(mainStopwatch, label: timerLabel)
laps.removeAll()
lapsTableView.reloadData()
}

fileprivate func resetLapTimer() {

CSPIT 104
Ce373:Mobile Application Development 16CE068

resetTimer(lapStopwatch, label: lapTimerLabel)


}

fileprivate func resetTimer(_ stopwatch: Stopwatch, label: UILabel) {


stopwatch.timer.invalidate()
stopwatch.counter = 0.0
label.text = "00:00:00"
}

@objc func updateMainTimer() {


updateTimer(mainStopwatch, label: timerLabel)
}

@objc func updateLapTimer() {


updateTimer(lapStopwatch, label: lapTimerLabel)
}

func updateTimer(_ stopwatch: Stopwatch, label: UILabel) {


stopwatch.counter = stopwatch.counter + 0.035

var minutes: String = "\((Int)(stopwatch.counter / 60))"


if (Int)(stopwatch.counter / 60) < 10 {
minutes = "0\((Int)(stopwatch.counter / 60))"
}

var seconds: String = String(format: "%.2f", (stopwatch.counter.truncatingRemainder(dividingBy:


60)))
if stopwatch.counter.truncatingRemainder(dividingBy: 60) < 10 {
seconds = "0" + seconds
}

label.text = minutes + ":" + seconds


}
}

// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return laps.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let identifier: String = "lapCell"
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: identifier, for:
indexPath)

if let labelNum = cell.viewWithTag(11) as? UILabel {


labelNum.text = "Lap \(laps.count - (indexPath as NSIndexPath).row)"
}
if let labelTimer = cell.viewWithTag(12) as? UILabel {
labelTimer.text = laps[laps.count - (indexPath as NSIndexPath).row - 1]
}

return cell
}
}

// MARK: - Extension
fileprivate extension Selector {

CSPIT 105
Ce373:Mobile Application Development 16CE068

static let updateMainTimer = #selector(ViewController.updateMainTimer)


static let updateLapTimer = #selector(ViewController.updateLapTimer)
}

Stopwatch.swift

import Foundation

class Stopwatch: NSObject {


var counter: Double
var timer: Timer

override init() {
counter = 0.0
timer = Timer()
}
}

Output:

CSPIT 106
Ce373:Mobile Application Development 16CE068

 To do
Utils.swift
import Foundation

func dateFromString(_ date: String) -> Date? {


let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.date(from: date)
}

func stringFromDate(_ date: Date) -> String {


let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.string(from: date)
}

ToDoItem.swift
import Foundation

class ToDoItem: NSObject {


var id: String
var image: String
var title: String
var date: Date

init(id: String, image: String, title: String, date: Date) {


self.id = id
self.image = image
self.title = title

CSPIT 107
Ce373:Mobile Application Development 16CE068

self.date = date
}
}

ViewController.swift
import UIKit

var todos: [ToDoItem] = []

class ViewController: UIViewController {

@IBOutlet weak var todoTableView: UITableView!

override func viewDidLoad() {


super.viewDidLoad()

navigationItem.leftBarButtonItem = editButtonItem

todos = [ToDoItem(id: "1", image: "child-selected", title: "Go to Disney", date:


dateFromString("2014-10-20")!),
ToDoItem(id: "2", image: "shopping-cart-selected", title: "Cicso Shopping", date:
dateFromString("2014-10-28")!),
ToDoItem(id: "3", image: "phone-selected", title: "Phone to Jobs", date:
dateFromString("2014-10-30")!),
ToDoItem(id: "4", image: "travel-selected", title: "Plan to Europe", date: dateFromString("2014-
10-31")!)]
}

override func viewWillAppear(_ animated: Bool) {


super.viewWillAppear(animated)
todoTableView.reloadData()
}
func setMessageLabel(_ messageLabel: UILabel, frame: CGRect, text: String, textColor: UIColor,
numberOfLines: Int, textAlignment: NSTextAlignment, font: UIFont) {
messageLabel.frame = frame
messageLabel.text = text
messageLabel.textColor = textColor
messageLabel.numberOfLines = numberOfLines
messageLabel.textAlignment = textAlignment
messageLabel.font = font
messageLabel.sizeToFit()
}

func setCellWithTodoItem(_ cell: UITableViewCell, todo: ToDoItem) {


let imageView: UIImageView = cell.viewWithTag(11) as! UIImageView
let titleLabel: UILabel = cell.viewWithTag(12) as! UILabel
let dateLabel: UILabel = cell.viewWithTag(13) as! UILabel

imageView.image = UIImage(named: todo.image)


titleLabel.text = todo.title
dateLabel.text = stringFromDate(todo.date)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {


if segue.identifier == "editTodo" {
let vc = segue.destination as! DetailViewController
let indexPath = todoTableView.indexPathForSelectedRow
if let indexPath = indexPath {
vc.todo = todos[(indexPath as NSIndexPath).row]

CSPIT 108
Ce373:Mobile Application Development 16CE068

}
}
}
}

extension ViewController: UITableViewDataSource {


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

if todos.count != 0 {
return todos.count
} else {
let messageLabel: UILabel = UILabel()

setMessageLabel(messageLabel, frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width,


height: self.view.bounds.size.height), text: "No data is currently available.", textColor: UIColor.black,
numberOfLines: 0, textAlignment: NSTextAlignment.center, font: UIFont(name:"Palatino-Italic", size:
20)!)

self.todoTableView.backgroundView = messageLabel
self.todoTableView.separatorStyle = UITableViewCellSeparatorStyle.none

return 0
}
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let cellIdentifier: String = "todoCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)

setCellWithTodoItem(cell, todo: todos[(indexPath as NSIndexPath).row])

return cell
}
}

extension ViewController: UITableViewDelegate {


// Edit mode
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
todoTableView.setEditing(editing, animated: true)
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle,


forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
todos.remove(at: (indexPath as NSIndexPath).row)
todoTableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}

// Move the cell


func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return self.isEditing
}

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to


destinationIndexPath: IndexPath) {
let todo = todos.remove(at: (sourceIndexPath as NSIndexPath).row)
todos.insert(todo, at: (destinationIndexPath as NSIndexPath).row)

CSPIT 109
Ce373:Mobile Application Development 16CE068

}
}

DetailViewController.swift
import UIKit
class DetailViewController: UIViewController {

@IBOutlet weak var childButton: UIButton!


@IBOutlet weak var phoneButton: UIButton!
@IBOutlet weak var shoppingCartButton: UIButton!
@IBOutlet weak var travelButton: UIButton!
@IBOutlet weak var todoTitleLabel: UITextField!
@IBOutlet weak var todoDatePicker: UIDatePicker!
var todo: ToDoItem?
override func viewDidLoad() {
super.viewDidLoad()
if let todo = todo {
self.title = "Edit Todo"
if todo.image == "child-selected"{
childButton.isSelected = true
}
else if todo.image == "phone-selected"{
phoneButton.isSelected = true
}
else if todo.image == "shopping-cart-selected"{
shoppingCartButton.isSelected = true
}
else if todo.image == "travel-selected"{
travelButton.isSelected = true
}
todoTitleLabel.text = todo.title
todoDatePicker.setDate(todo.date, animated: false)
} else {
title = "New Todo"
childButton.isSelected = true
}
}
// MARK: type select
@IBAction func selectChild(_ sender: AnyObject) {
resetButtons()
childButton.isSelected = true
}
@IBAction func selectPhone(_ sender: AnyObject) {
resetButtons()
phoneButton.isSelected = true
}
@IBAction func selectShoppingCart(_ sender: AnyObject) {
resetButtons()
shoppingCartButton.isSelected = true
}
@IBAction func selectTravel(_ sender: AnyObject) {
resetButtons()
travelButton.isSelected = true
}
func resetButtons() {
childButton.isSelected = false
phoneButton.isSelected = false
shoppingCartButton.isSelected = false

CSPIT 110
Ce373:Mobile Application Development 16CE068

travelButton.isSelected = false
}
@IBAction func tapDone(_ sender: AnyObject) {
var image = ""
if childButton.isSelected {
image = "child-selected"
}
else if phoneButton.isSelected {
image = "phone-selected"
}
else if shoppingCartButton.isSelected {
image = "shopping-cart-selected"
}
else if travelButton.isSelected {
image = "travel-selected"
}
if let todo = todo {
todo.image = image
todo.title = todoTitleLabel.text!
todo.date = todoDatePicker.date
} else {
let uuid = UUID().uuidString
todo = ToDoItem(id: uuid, image: image, title: todoTitleLabel.text!, date: todoDatePicker.date)
todos.append(todo!)
}
let _ = navigationController?.popToRootViewController(animated: true)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
view.endEditing(true)
}
}
Output:

CSPIT 111
Ce373:Mobile Application Development 16CE068

 CandySearch

MasterViewController.shift
import UIKit

class MasterViewController: UITableViewController {

// MARK: - Properties
var detailViewController: DetailViewController? = nil
var candies = [Candy]()
var filteredCandies = [Candy]()
let searchController = UISearchController(searchResultsController: nil)

// MARK: - View Setup


override func viewDidLoad() {
super.viewDidLoad()

candies = [
Candy(category:"Chocolate", name:"Chocolate Bar"),
Candy(category:"Chocolate", name:"Chocolate Chip"),
Candy(category:"Chocolate", name:"Dark Chocolate"),
Candy(category:"Hard", name:"Lollipop"),
Candy(category:"Hard", name:"Candy Cane"),
Candy(category:"Hard", name:"Jaw Breaker"),
Candy(category:"Other", name:"Caramel"),
Candy(category:"Other", name:"Sour Chew"),
Candy(category:"Other", name:"Gummi Bear")
]

setupSearchController()

if let splitViewController = splitViewController {


let controllers = splitViewController.viewControllers

CSPIT 112
Ce373:Mobile Application Development 16CE068

detailViewController = (controllers[controllers.count - 1] as!


UINavigationController).topViewController as? DetailViewController
}
}

// MARK: - Search Controller Setup


func setupSearchController () {
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
searchController.searchBar.scopeButtonTitles = ["All", "Chocolate", "Hard", "Other"]
searchController.searchBar.delegate = self
if #available(iOS 11, *) {
self.navigationItem.searchController = searchController
self.navigationItem.searchController?.isActive = true
self.navigationItem.hidesSearchBarWhenScrolling = false
} else {
tableView.tableHeaderView = searchController.searchBar
}
}

func filterContentForSearchText(_ searchText: String, scope: String = "All") {


filteredCandies = candies.filter { candy in
if !(candy.category == scope) && scope != "All" {
return false
}

return candy.name.lowercased().contains(searchText.lowercased()) || searchText == ""


}

tableView.reloadData()
}

override func viewWillAppear(_ animated: Bool) {


clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
}

override func didReceiveMemoryWarning() {


super.didReceiveMemoryWarning()
}

// MARK: - Table View


override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive {
return filteredCandies.count
}
return candies.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->


UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

let candy: Candy

CSPIT 113
Ce373:Mobile Application Development 16CE068

if searchController.isActive {
candy = filteredCandies[(indexPath as NSIndexPath).row]
} else {
candy = candies[(indexPath as NSIndexPath).row]
}
cell.textLabel!.text = candy.name
cell.detailTextLabel!.text = candy.category
return cell
}

// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let candy: Candy
if searchController.isActive {
candy = filteredCandies[(indexPath as NSIndexPath).row]
} else {
candy = candies[(indexPath as NSIndexPath).row]
}
let controller = (segue.destination as! UINavigationController).topViewController as!
DetailViewController
controller.detailCandy = candy
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}

extension MasterViewController: UISearchResultsUpdating {


func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}

extension MasterViewController: UISearchBarDelegate {


func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope:
Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}

DetailViewController.shift
import UIKit

class DetailViewController: UIViewController {

@IBOutlet weak var detailDescriptionLabel: UILabel!


@IBOutlet weak var candyImageView: UIImageView!

var detailCandy: Candy? {


didSet {
configureView()
}

CSPIT 114
Ce373:Mobile Application Development 16CE068

}
func configureView() {
if let detailCandy = detailCandy {
if let detailDescriptionLabel = detailDescriptionLabel, let candyImageView = candyImageView {
detailDescriptionLabel.text = detailCandy.name
candyImageView.image = UIImage(named: detailCandy.name)
title = detailCandy.category
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Candy.shift
import Foundation

class Candy {
var category : String
var name : String

init(category: String, name: String) {


self.category = category
self.name = name
}
}

Output:

CSPIT 115
Ce373:Mobile Application Development 16CE068

PRACTICAL 13
13) Aim:Implementation of PokedexGo, Simple RSS
Reader,FacebookMe, Interests, Photoscroll & Animation in iOS

 PokedexGo

MasterViewControllerTableViewController.swift
import UIKit
import RxSwift
import RxCocoa

protocol PokemonSelectionDelegate: class {


func pokemonSelected(_ newPokemon: Pokemon)
}

class MasterViewController: UITableViewController {


var pokemons = LibraryAPI.sharedInstance.getPokemons()
var filteredPokemons = [Pokemon]()
weak var delegate: PokemonSelectionDelegate?

fileprivate let disposeBag = DisposeBag()

@IBOutlet weak var searchBar: UISearchBar!

override func viewDidLoad() {


super.viewDidLoad()
setupUI()

filteredPokemons = pokemons
}

fileprivate func setupUI() {


self.title = "精灵列表"

definesPresentationContext = true

searchBar
.rx.text
.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(
onNext: { [unowned self] query in
if query?.characters.count == 0 {
self.filteredPokemons = self.pokemons
} else {
self.filteredPokemons = self.pokemons.filter{ $0.name.hasPrefix(query!) }
}
self.tableView.reloadData()
})
.addDisposableTo(disposeBag)
}

func dismissKeyboard() {
view.endEditing(true)

CSPIT 116
Ce373:Mobile Application Development 16CE068

// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) ->
CGFloat {
return 140
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


let pokemon = self.filteredPokemons[(indexPath as NSIndexPath).row]

delegate?.pokemonSelected(pokemon)

if let detailViewController = self.delegate as? DetailViewController {


splitViewController?.showDetailViewController(detailViewController.navigationController!, sender:
nil)
}
}

// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredPokemons.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->


UITableViewCell {
let identifier = "Cell"

let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as!


MasterTableViewCell
let pokemon = filteredPokemons[(indexPath as NSIndexPath).row]

cell.awakeFromNib(pokemon.id, name: pokemon.name, pokeImageUrl: pokemon.pokeImgUrl)

return cell
}
}

LibraryAPI.swift
import UIKit
class LibraryAPI: NSObject {
static let sharedInstance = LibraryAPI()
let persistencyManager = PersistencyManager()
fileprivate override init() {
super.init()
NotificationCenter.default.addObserver(self, selector:#selector(LibraryAPI.downloadImage(_:)),
name: NSNotification.Name(rawValue: downloadImageNotification), object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func getPokemons() -> [Pokemon] {
return pokemons
}
func downloadImg(_ url: String) -> (UIImage) {

CSPIT 117
Ce373:Mobile Application Development 16CE068

let aUrl = URL(string: url)


let data = try? Data(contentsOf: aUrl!)
let image = UIImage(data: data!)
return image!
}

func downloadImage(_ notification: Notification) {


// retrieve info from notification
let userInfo = (notification as NSNotification).userInfo as! [String: AnyObject]
let pokeImageView = userInfo["pokeImageView"] as! UIImageView?
let pokeImageUrl = userInfo["pokeImageUrl"] as! String

if let imageViewUnWrapped = pokeImageView {


imageViewUnWrapped.image = persistencyManager.getImage(URL(string:
pokeImageUrl)!.lastPathComponent)
if imageViewUnWrapped.image == nil {

DispatchQueue.global().async {
let downloadedImage = self.downloadImg(pokeImageUrl as String)
DispatchQueue.main.async {
imageViewUnWrapped.image = downloadedImage
self.persistencyManager.saveImage(downloadedImage, filename: URL(string:
pokeImageUrl)!.lastPathComponent)
}
}
}
}
}
}

PersistencyManager.swift
import UIKit

class PersistencyManager: NSObject {


func saveImage(_ image: UIImage, filename: String) {
let path = NSHomeDirectory() + "/Documents/\(filename)"
let data = UIImagePNGRepresentation(image)
try? data!.write(to: URL(fileURLWithPath: path), options: [.atomic])
}

func getImage(_ filename: String) -> UIImage? {


let path = NSHomeDirectory() + "/Documents/\(filename)"

do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .uncachedRead)
return UIImage(data: data)
} catch {
return nil
}
}
}

MasterViewControllerTableViewController.swift

import UIKit
import RxSwift

CSPIT 118
Ce373:Mobile Application Development 16CE068

import RxCocoa

protocol PokemonSelectionDelegate: class {


func pokemonSelected(_ newPokemon: Pokemon)
}

class MasterViewController: UITableViewController {


var pokemons = LibraryAPI.sharedInstance.getPokemons()
var filteredPokemons = [Pokemon]()
weak var delegate: PokemonSelectionDelegate?

fileprivate let disposeBag = DisposeBag()

@IBOutlet weak var searchBar: UISearchBar!

override func viewDidLoad() {


super.viewDidLoad()
setupUI()

filteredPokemons = pokemons
}

fileprivate func setupUI() {


self.title = "精灵列表"

definesPresentationContext = true

searchBar
.rx.text
.throttle(0.5, scheduler: MainScheduler.instance)
.subscribe(
onNext: { [unowned self] query in
if query?.characters.count == 0 {
self.filteredPokemons = self.pokemons
} else {
self.filteredPokemons = self.pokemons.filter{ $0.name.hasPrefix(query!) }
}
self.tableView.reloadData()
})
.addDisposableTo(disposeBag)
}

func dismissKeyboard() {
view.endEditing(true)
}

// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) ->
CGFloat {
return 140
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


let pokemon = self.filteredPokemons[(indexPath as NSIndexPath).row]

delegate?.pokemonSelected(pokemon)

if let detailViewController = self.delegate as? DetailViewController {

CSPIT 119
Ce373:Mobile Application Development 16CE068

splitViewController?.showDetailViewController(detailViewController.navigationController!, sender:
nil)
}
}

// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredPokemons.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->


UITableViewCell {
let identifier = "Cell"

let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as!


MasterTableViewCell
let pokemon = filteredPokemons[(indexPath as NSIndexPath).row]
cell.awakeFromNib(pokemon.id, name: pokemon.name, pokeImageUrl: pokemon.pokeImgUrl)
return cell
}
}

DetailViewController.swift
import UIKit
class DetailViewController: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var nameIDLabel: UILabel!
@IBOutlet weak var pokeImageView: UIImageView!
@IBOutlet weak var pokeInfoLabel: UILabel!
var pokemon: Pokemon! {
didSet (newPokemon) {
self.refreshUI()
}
}
override func viewDidLoad() {
refreshUI()
super.viewDidLoad()
}
func refreshUI() {
nameIDLabel?.text = pokemon.name + (pokemon.id < 10 ? " #00\(pokemon.id)" : pokemon.id < 100
? " #0\(pokemon.id)" : " #\(pokemon.id)")
pokeImageView?.image = LibraryAPI.sharedInstance.downloadImg(pokemon.pokeImgUrl)
pokeInfoLabel?.text = pokemon.detailInfo

self.title = pokemon.name
}
}

extension DetailViewController: PokemonSelectionDelegate {


func pokemonSelected(_ newPokemon: Pokemon) {
pokemon = newPokemon
}
}

CSPIT 120
Ce373:Mobile Application Development 16CE068

Pokemon.swift
import UIKit

enum PokeType {
case normal
case fire
case water
case electric
case grass
case ice
case fighting
case poison
case ground
case flying
case psychic
case bug
case rock
case ghost
case dragon
case dark
case steel
case fairy
}

class Pokemon: NSObject {


let name: String
let id: Int
let detailInfo: String
let type: [PokeType]
let weak: [PokeType]
let pokeImgUrl: String

init(name: String, id: Int, detailInfo: String, type: [PokeType], weak: [PokeType], pokeImgUrl: String) {
self.name = name
self.id = id
self.detailInfo = detailInfo
self.type = type
self.weak = weak
self.pokeImgUrl = pokeImgUrl
}
}

MasterTableViewCell.swift
import UIKit
class MasterTableViewCell: UITableViewCell {
@IBOutlet weak var idLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var pokeImageView: UIImageView!
fileprivate var indicator: UIActivityIndicatorView!
func awakeFromNib(_ id: Int, name: String, pokeImageUrl: String) {
super.awakeFromNib()

CSPIT 121
Ce373:Mobile Application Development 16CE068

setupUI(id, name: name)


setupNotification(pokeImageUrl)
}
deinit {
pokeImageView.removeObserver(self, forKeyPath: "image")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}

fileprivate func setupUI(_ id: Int, name: String) {


idLabel.text = NSString(format: "#%03d", id) as String
nameLabel.text = name
pokeImageView.image = UIImage(named: "default_img")

indicator = UIActivityIndicatorView()
indicator.center = CGPoint(x: pokeImageView.bounds.midX, y: pokeImageView.bounds.midY)
indicator.activityIndicatorViewStyle = .whiteLarge
indicator.startAnimating()
pokeImageView.addSubview(indicator)

pokeImageView.addObserver(self, forKeyPath: "image", options: [], context: nil)


}

fileprivate func setupNotification(_ pokeImageUrl: String) {


NotificationCenter.default.post(name: Notification.Name(rawValue: downloadImageNotification),
object: self, userInfo: ["pokeImageView":pokeImageView, "pokeImageUrl" : pokeImageUrl])
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change:


[NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "image" {
indicator.stopAnimating()
}
}
}

PokemonConstants.swift
import Foundation

let pokemons = [
Pokemon(name: "妙蛙种子", id: 1,
detailInfo: "妙蛙种子经常在阳光下酣睡。它背上有个种子,通过吸收阳光渐渐长大。",
type: [PokeType.grass, PokeType.poison],
weak: [PokeType.fire, PokeType.flying, PokeType.ice, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/001.png"),

Pokemon(name: "妙蛙草", id: 2,


detailInfo: "在它的背上有一个花苞。妙蛙草的四肢非常粗壮,这样才能支撑它的体重。如果它开始经
常晒太阳,表明它将要开花了。",
type: [PokeType.grass, PokeType.poison],
weak: [PokeType.fire, PokeType.flying, PokeType.ice, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/002.png"),

Pokemon(name: "妙蛙花", id: 3,


detailInfo: "妙蛙花的背上有一朵巨大的鲜花。如果它吸收养分、晒太阳,花的颜色就会变得非常鲜
艳。花的香气还可以抚慰伤感的人。",

CSPIT 122
Ce373:Mobile Application Development 16CE068

type: [PokeType.grass, PokeType.poison],


weak: [PokeType.fire, PokeType.flying, PokeType.ice, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/003.png"),

Pokemon(name: "小火龙", id: 4,


detailInfo: "它尾巴上的火焰能够表现出它的情绪。当它开心时,火焰会摇曳。如果它被激怒,火焰就
会猛烈地燃烧。",
type: [PokeType.fire],
weak: [PokeType.ground, PokeType.rock, PokeType.water],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/004.png"),

Pokemon(name: "火恐龙", id: 5,


detailInfo: "火恐龙会用它锋利的爪子残忍地撕裂敌人。如果它的敌人十分强壮,它就会变得好斗。在
这种情况下,它尾巴上的火焰会变成带点蓝色的亮白。",
type: [PokeType.fire],
weak: [PokeType.ground, PokeType.rock, PokeType.water],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/005.png"),

Pokemon(name: "喷火龙", id: 6,


detailInfo: "喷火龙在空中飞行,寻找强大的对手。它吐出的火焰温度极高,可以融化一切物体。然
而,它从来不攻击任何比它弱小的对手。",
type: [PokeType.fire, PokeType.flying],
weak: [PokeType.ground, PokeType.electric, PokeType.water],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/006.png"),

Pokemon(name: "杰尼龟", id: 7,


detailInfo: "杰尼龟的壳不仅仅用来保护自己。它圆形的壳和表明的沟壑减小了它在水中的阻力,让这
只神奇宝贝能高速游泳。",
type: [PokeType.water],
weak: [PokeType.electric, PokeType.grass],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/007.png"),

Pokemon(name: "卡咪龟", id: 8,


detailInfo: "蓬松的毛覆盖的大大的尾巴的颜色会随着年龄的增长而变深。背甲上的伤痕是强者的证
明。",
type: [PokeType.water],
weak: [PokeType.electric, PokeType.grass],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/008.png"),

Pokemon(name: "水箭龟", id: 9,


detailInfo: "水箭龟背甲上伸出喷射口,可以精确瞄准,水弹可以击中 50 米外的空罐子。",
type: [PokeType.water],
weak: [PokeType.electric, PokeType.grass],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/009.png"),

Pokemon(name: "绿毛虫", id: 10,


detailInfo: "有着很好的食欲,可以瞬间吃掉比身体还大的叶子。触角会释放出强烈的臭味。",
type: [PokeType.bug],
weak: [PokeType.fire, PokeType.flying, PokeType.rock],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/010.png"),

Pokemon(name: "铁甲蛹", id: 11,


detailInfo: "身体外面的壳有铁板那么硬。不怎么移动是因为里面柔软的身体正在准备进化。",
type: [PokeType.bug],
weak: [PokeType.fire, PokeType.flying, PokeType.rock],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/011.png"),

CSPIT 123
Ce373:Mobile Application Development 16CE068

Pokemon(name: "巴大蝶", id: 12,


detailInfo: "寻找美味花蜜的能力非常好,离家 10 公里外开花的花蜜也可以找到并运回来。",
type: [PokeType.bug, PokeType.flying],
weak: [PokeType.fire, PokeType.flying, PokeType.rock, PokeType.electric, PokeType.ice],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/012.png"),

Pokemon(name: "独角虫", id: 13,


detailInfo: "嗅觉非常灵敏,可以用大大的红鼻子从讨厌的叶子中嗅出自己喜欢的叶子的味道。",
type: [PokeType.bug, PokeType.poison],
weak: [PokeType.fire, PokeType.flying, PokeType.rock, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/013.png"),

Pokemon(name: "铁壳蛹", id: 14,


detailInfo: "挂在树上几乎一动不动是为了进化忙碌地准备着,证据是身体在逐渐变热。",
type: [PokeType.bug, PokeType.poison],
weak: [PokeType.fire, PokeType.flying, PokeType.rock, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/014.png"),

Pokemon(name: "大针蜂", id: 15,


detailInfo: "保护领地的意识非常强,为了安全不会让任何人接近自己的巢穴,生气的话就会一起攻上
来。",
type: [PokeType.bug, PokeType.poison],
weak: [PokeType.fire, PokeType.flying, PokeType.rock, PokeType.psychic],
pokeImgUrl: "http://assets.pokemon.com/assets/cms2/img/pokedex/full/015.png")
]
Constants.swift
import Foundation
let downloadImageNotification = "PokedexGoDownloadImageNotification"

Output:

 Simple Rss Reader

AppDelegate.swift

import UIKit

CSPIT 124
Ce373:Mobile Application Development 16CE068

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {


// Sent when the application is about to move from active to inactive state. This can occur for
certain types of temporary interruptions (such as an incoming phone call or SMS message) or when
the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame
rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// Use this method to release shared resources, save user data, invalidate timers, and store enough
application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of
applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the inactive state; here you can undo many
of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {


// Restart any tasks that were paused (or not yet started) while the application was inactive. If the
application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
}

NewsTableViewController.swift
import UIKit

class NewsTableViewController: UITableViewController {

fileprivate let feedParser = FeedParser()


fileprivate let feedURL = "http://www.apple.com/main/rss/hotnews/hotnews.rss"

fileprivate var rssItems: [(title: String, description: String, pubDate: String)]?


fileprivate var cellStates: [CellState]?

override func viewDidLoad() {


super.viewDidLoad()

tableView.estimatedRowHeight = 140
tableView.rowHeight = UITableViewAutomaticDimension

CSPIT 125
Ce373:Mobile Application Development 16CE068

tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine

feedParser.parseFeed(feedURL: feedURL) { [weak self] rssItems in


self?.rssItems = rssItems
self?.cellStates = Array(repeating: .collapsed, count: rssItems.count)

DispatchQueue.main.async {
self?.tableView.reloadSections(IndexSet(integer: 0), with: .none)
}
}
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {


return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let rssItems = rssItems else {
return 0
}
return rssItems.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->


UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as!
NewsTableViewCell

if let item = rssItems?[indexPath.row] {


(cell.titleLabel.text, cell.descriptionLabel.text, cell.dateLabel.text) = (item.title, item.description,
item.pubDate)

if let cellState = cellStates?[indexPath.row] {


cell.descriptionLabel.numberOfLines = cellState == .expanded ? 0: 4
}
}

return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)

let cell = tableView.cellForRow(at: indexPath) as! NewsTableViewCell

tableView.beginUpdates()
cell.descriptionLabel.numberOfLines = cell.descriptionLabel.numberOfLines == 4 ? 0 : 4
cellStates?[indexPath.row] = cell.descriptionLabel.numberOfLines == 4 ? .collapsed : .expanded
tableView.endUpdates()
}
}

NewsTableViewCell.swift
import UIKit
enum CellState {
case expanded
case collapsed

CSPIT 126
Ce373:Mobile Application Development 16CE068

class NewsTableViewCell: UITableViewCell {


@IBOutlet weak var titleLabel:UILabel!

@IBOutlet weak var descriptionLabel:UILabel! {


didSet {
descriptionLabel.numberOfLines = 4
}
}
@IBOutlet weak var dateLabel:UILabel!

override func awakeFromNib() {


super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Output:

CSPIT 127
Ce373:Mobile Application Development 16CE068

 FaceBookMe
AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {

window = UIWindow(frame: UIScreen.main.bounds)


window?.rootViewController = UINavigationController(rootViewController: FBMeViewController())
window?.makeKeyAndVisible()

return true
}
}

Specs.swift
import UIKit

public struct Specs {


public struct Color {
public let tint = UIColor(hex: 0x3b5998)

CSPIT 128
Ce373:Mobile Application Development 16CE068

public let red = UIColor.red


public let white = UIColor.white
public let black = UIColor.black
public let gray = UIColor.lightGray
}

public struct FontSize {


public let tiny: CGFloat = 10
public let small: CGFloat = 12
public let regular: CGFloat = 14
public let large: CGFloat = 16
}

public struct Font {


private static let regularName = "Helvetica Neue"
private static let boldName = "Helvetica Neue Bold"
public let tiny = UIFont(name: regularName, size: Specs.fontSize.tiny)
public let small = UIFont(name: regularName, size: Specs.fontSize.small)
public let regular = UIFont(name: regularName, size: Specs.fontSize.regular)
public let large = UIFont(name: regularName, size: Specs.fontSize.large)
public let smallBold = UIFont(name: boldName, size: Specs.fontSize.small)
public let regularBold = UIFont(name: boldName, size: Specs.fontSize.regular)
public let largeBold = UIFont(name: boldName, size: Specs.fontSize.large)
}

public struct ImageName {


public let friends = "fb_friends"
public let events = "fb_events"
public let groups = "fb_groups"
public let education = "fb_education"
public let townHall = "fb_town_hall"
public let instantGames = "fb_games"
public let settings = "fb_settings"
public let privacyShortcuts = "fb_privacy_shortcuts"
public let helpSupport = "fb_help_and_support"
public let placeholder = "fb_placeholder"
}

public static var color: Color {


return Color()
}

public static var fontSize: FontSize {


return FontSize()
}

public static var font: Font {


return Font()
}

public static var imageName: ImageName {


return ImageName()
}
}

UIColor+Extension.swift
import UIKit

CSPIT 129
Ce373:Mobile Application Development 16CE068

public extension UIColor {


convenience init(r: Int, g: Int, b: Int, a: CGFloat) {
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: a)
}

convenience init(hex: Int) {


self.init(r: (hex & 0xff0000) >> 16, g: (hex & 0xff00) >> 8, b: (hex & 0xff), a: 1)
}
}
TableKeys.swift

import Foundation

public struct TableKeys {


static let Section = "section"
static let Rows = "rows"
static let ImageName = "imageName"
static let Title = "title"
static let SubTitle = "subTitle"
static let seeMore = "See More..."
static let addFavorites = "Add Favorites..."
static let logout = "Log Out"

static func populate(withUser user: FBMeUser) -> [[String: Any]] {


return [
[
TableKeys.Rows: [
[TableKeys.ImageName: user.avatarName, TableKeys.Title: user.name, TableKeys.SubTitle:
"View your profile"]
]
],
[
TableKeys.Rows: [
[TableKeys.ImageName: Specs.imageName.friends, TableKeys.Title: "Friends"],
[TableKeys.ImageName: Specs.imageName.events, TableKeys.Title: "Events"],
[TableKeys.ImageName: Specs.imageName.groups, TableKeys.Title: "Groups"],
[TableKeys.ImageName: Specs.imageName.education, TableKeys.Title: user.education],
[TableKeys.ImageName: Specs.imageName.townHall, TableKeys.Title: "Town Hall"],
[TableKeys.ImageName: Specs.imageName.instantGames, TableKeys.Title: "Instant Games"],
[TableKeys.Title: TableKeys.seeMore]
]
],
[
TableKeys.Section: "FAVORITES",
TableKeys.Rows: [
[TableKeys.Title: TableKeys.addFavorites]
]
],
[
TableKeys.Rows: [
[TableKeys.ImageName: Specs.imageName.settings, TableKeys.Title: "Settings"],
[TableKeys.ImageName: Specs.imageName.privacyShortcuts, TableKeys.Title: "Privacy
Shortcuts"],
[TableKeys.ImageName: Specs.imageName.helpSupport, TableKeys.Title: "Help and Support"]
]
],
[
TableKeys.Rows: [
[TableKeys.Title: TableKeys.logout]

CSPIT 130
Ce373:Mobile Application Development 16CE068

]
]
]
}
}

FBMeBaseViewController.swift
import UIKit

class FBMeBaseViewController: UIViewController {


override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Specs.color.gray
}
}

FBMeViewController.swift
import UIKit

class FBMeViewController: FBMeBaseViewController {

typealias RowModel = [String: String]

fileprivate var user: FBMeUser {


get {
return FBMeUser(name: "BayMax", education: "CMU")
}
}

fileprivate var tableViewDataSource: [[String: Any]] {


get {
return TableKeys.populate(withUser: user)
}
}

private let tableView: UITableView = {


let view = UITableView(frame: .zero, style: .grouped)
view.register(FBMeBaseCell.self, forCellReuseIdentifier: FBMeBaseCell.identifier)
return view
}()

override func viewDidLoad() {


super.viewDidLoad()

title = "Facebook"
navigationController?.navigationBar.barTintColor = Specs.color.tint

tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)

// Set layout for tableView.


tableView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[tableView]-0-|",
options: .directionLeadingToTrailing, metrics: nil, views: ["tableView": tableView]))

CSPIT 131
Ce373:Mobile Application Development 16CE068

view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[tableView]-0-|",
options: .directionLeadingToTrailing, metrics: nil, views: ["tableView": tableView]))
}

fileprivate func rows(at section: Int) -> [Any] {


return tableViewDataSource[section][TableKeys.Rows] as! [Any]
}

fileprivate func title(at section: Int) -> String? {


return tableViewDataSource[section][TableKeys.Section] as? String
}

fileprivate func rowModel(at indexPath: IndexPath) -> RowModel {


return rows(at: indexPath.section)[indexPath.row] as! RowModel
}
}

extension FBMeViewController: UITableViewDataSource {


func numberOfSections(in tableView: UITableView) -> Int {
return tableViewDataSource.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


return rows(at: section).count
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {


return title(at: section)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let modelForRow = rowModel(at: indexPath)
var cell = UITableViewCell()

guard let title = modelForRow[TableKeys.Title] else {


return cell
}

if title == user.name {
cell = UITableViewCell.init(style: .subtitle, reuseIdentifier: nil)
} else {
cell = tableView.dequeueReusableCell(withIdentifier: FBMeBaseCell.identifier, for: indexPath)
}

cell.textLabel?.text = title

if let imageName = modelForRow[TableKeys.ImageName] {


cell.imageView?.image = UIImage(named: imageName)
} else if title != TableKeys.logout {
cell.imageView?.image = UIImage(named: Specs.imageName.placeholder)
}

if title == user.name {
cell.detailTextLabel?.text = modelForRow[TableKeys.SubTitle]
}

return cell
}
}

CSPIT 132
Ce373:Mobile Application Development 16CE068

extension FBMeViewController: UITableViewDelegate {


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let modelForRow = rowModel(at: indexPath)

guard let title = modelForRow[TableKeys.Title] else {


return 0.0
}

if title == user.name {
return 64.0
} else {
return 44.0
}
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath:


IndexPath) {
let modelForRow = rowModel(at: indexPath)

guard let title = modelForRow[TableKeys.Title] else {


return
}

if title == TableKeys.seeMore || title == TableKeys.addFavorites {


cell.textLabel?.textColor = Specs.color.tint
cell.accessoryType = .none
} else if title == TableKeys.logout {
cell.textLabel?.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true
cell.textLabel?.textColor = Specs.color.red
cell.textLabel?.textAlignment = .center
cell.accessoryType = .none
} else {
cell.accessoryType = .disclosureIndicator
}
}
}

FBMeBaseCell.swift
import UIKit

class FBMeBaseCell: UITableViewCell {


static let identifier = "FBMeBaseCell"

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {


super.init(style: .default, reuseIdentifier: reuseIdentifier)

backgroundColor = Specs.color.white
textLabel?.textColor = Specs.color.black
textLabel?.font = Specs.font.large

detailTextLabel?.font = Specs.font.small
detailTextLabel?.textColor = Specs.color.gray
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

CSPIT 133
Ce373:Mobile Application Development 16CE068

FBMeUser.swift
import UIKit

class FBMeUser {
var name: String
var avatarName: String
var education: String

init(name: String, avatarName: String = "bayMax", education: String) {


self.name = name
self.avatarName = avatarName
self.education = education
}
}

Output:

 Interests

AppDelegate.swift
import UIKit

@UIApplicationMain

CSPIT 134
Ce373:Mobile Application Development 16CE068

class AppDelegate: UIResponder, UIApplicationDelegate {


var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {


// Sent when the application is about to move from active to inactive state. This can occur for
certain types of temporary interruptions (such as an incoming phone call or SMS message) or when
the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame
rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// Use this method to release shared resources, save user data, invalidate timers, and store enough
application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of
applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the inactive state; here you can undo many
of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {


// Restart any tasks that were paused (or not yet started) while the application was inactive. If the
application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {


// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
}

}
InterestCollectionViewCell.swift
import UIKit

class InterestCollectionViewCell: UICollectionViewCell {


// MARK: - IBOutlets
@IBOutlet weak var featuredImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!

// MARK: - public API


var interest: Interest! {
didSet {
updateUI()
}
}

fileprivate func updateUI() {


titleLabel.text = interest.title
featuredImageView.image = interest.featuredImage
}

CSPIT 135
Ce373:Mobile Application Development 16CE068

// MARK: - refactor layout


override func layoutSubviews() {
super.layoutSubviews()

self.layer.cornerRadius = 8.0
self.clipsToBounds = true
}
}

Interest.swift
import UIKit

class Interest
{
// MARK: - Public API
var id = ""
var title = ""
var description = ""
var numberOfMembers = 0
var numberOfPosts = 0
var featuredImage: UIImage!

init(id: String, title: String, description: String, featuredImage: UIImage!)


{
self.id = id
self.title = title
self.description = description
self.featuredImage = featuredImage
numberOfMembers = 1
numberOfPosts = 1
}

// MARK: - Private

static func createInterests() -> [Interest]


{
return [
Interest(id: "r1", title: "We Love Traveling Around the World", description: "We love backpack and
adventures! We walked to Antartica yesterday, and camped with some cute pinguines, and talked
about this wonderful app idea. �⛺✨", featuredImage: UIImage(named: "r1")!),
Interest(id: "r2", title: "Romance Stories", description: "We love romantic stories. We walked to
Antartica yesterday, and camped with some cute pinguines, and talked about this wonderful app idea.
�⛺✨", featuredImage: UIImage(named: "r2")!),
Interest(id: "r3", title: "iOS Dev", description: "Create beautiful apps. We walked to Antartica
yesterday, and camped with some cute pinguines, and talked about this wonderful app idea. �⛺✨",
featuredImage: UIImage(named: "r3")!),
Interest(id: "r4", title: "Race", description: "Cars and aircrafts and boats and sky. We walked to
Antartica yesterday, and camped with some cute pinguines, and talked about this wonderful app idea.
�⛺✨", featuredImage: UIImage(named: "r4")!),
Interest(id: "r5", title: "Personal Development", description: "Meet life with full presence. We
walked to Antartica yesterday, and camped with some cute pinguines, and talked about this wonderful
app idea. �⛺✨", featuredImage: UIImage(named: "r5")!),
Interest(id: "r6", title: "Reading News", description: "Get up to date with breaking-news. We walked
to Antartica yesterday, and camped with some cute pinguines, and talked about this wonderful app
idea. �⛺✨", featuredImage: UIImage(named: "r6")!),
]

CSPIT 136
Ce373:Mobile Application Development 16CE068

}
}

HomeViewController.swift
import UIKit

class HomeViewController: UIViewController {


// MARK: - IBOutlets
@IBOutlet weak var backgroundImageView: UIImageView!
@IBOutlet weak var collectionView: UICollectionView!

fileprivate var interests = Interest.createInterests()

fileprivate struct Storyboard {


static let cellIdentifier = "Interest Cell"
}

override func viewDidLoad() {


super.viewDidLoad()
}

override var preferredStatusBarStyle : UIStatusBarStyle {


return .lightContent
}
}

// MARK: - UICollectionViewDataSource
extension HomeViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return interests.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) ->


UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Storyboard.cellIdentifier, for:
indexPath) as! InterestCollectionViewCell

cell.interest = interests[(indexPath as NSIndexPath).item]

return cell
}
}

// MARK: - UIScrollViewDelegate
extension HomeViewController: UIScrollViewDelegate {
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
let cellWidthWithSpace = layout.itemSize.width + layout.minimumLineSpacing

var offset = targetContentOffset.pointee

let index = (offset.x + scrollView.contentInset.left) / cellWidthWithSpace


let roundedIndex = round(index)

offset = CGPoint(x: roundedIndex * cellWidthWithSpace - scrollView.contentInset.left, y:


-scrollView.contentInset.top)

CSPIT 137
Ce373:Mobile Application Development 16CE068

}
}
Output:

 PhotoScroll
AppDelegate.shift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {

let pageControl = UIPageControl.appearance()


pageControl.pageIndicatorTintColor = UIColor.lightGray
pageControl.currentPageIndicatorTintColor = UIColor.red

return true
}
}

UIImage

import UIKit

extension UIImage {

CSPIT 138
Ce373:Mobile Application Development 16CE068

func thumbnailOfSize(_ size: CGFloat) -> UIImage {


UIGraphicsBeginImageContext(CGSize(width: size, height: size))
let rect = CGRect(x: 0.0, y: 0.0, width: size, height: size)
UIGraphicsBeginImageContext(rect.size)
draw(in: rect)
let thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
return thumbnail!
}
}

PhotoCell

import UIKit

class PhotoCell : UICollectionViewCell {


@IBOutlet weak var imageView: UIImageView!
}

CollectionViewController
import UIKit

class CollectionViewController: UICollectionViewController {


fileprivate let reuseIdentifier = "PhotoCell"
fileprivate let thumbnailSize:CGFloat = 70.0
fileprivate let sectionInsets = UIEdgeInsets(top: 10, left: 5.0, bottom: 10.0, right: 5.0)
fileprivate let photos = ["photo1", "photo2", "photo3", "photo4", "photo5"]

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {


if let cell = sender as? UICollectionViewCell,
let indexPath = collectionView?.indexPath(for: cell),
let zoomedPhotoViewController = segue.destination as? ZoomedPhotoViewController {
zoomedPhotoViewController.photoName = "photo\(indexPath.row + 1)"
}

if let cell = sender as? UICollectionViewCell,


let indexPath = collectionView?.indexPath(for: cell),
let photoCommentViewController = segue.destination as? PhotoCommentViewController {
photoCommentViewController.photoName = "photo\(indexPath.row + 1)"
}

if let cell = sender as? UICollectionViewCell,


let indexPath = collectionView?.indexPath(for: cell),
let managePageViewController = segue.destination as? ManagePageViewController {
managePageViewController.photos = photos
managePageViewController.currentIndex = indexPath.row
}
}
}

// MARK: UICollectionViewDataSource
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}

CSPIT 139
Ce373:Mobile Application Development 16CE068

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section:


Int) -> Int {
return photos.count
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)


-> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
as! PhotoCell
let fullSizedImage = UIImage(named:photos[indexPath.row])
cell.imageView.image = fullSizedImage?.thumbnailOfSize(thumbnailSize)
return cell
}
}

// MARK:UICollectionViewDelegateFlowLayout
extension CollectionViewController : UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:


UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: thumbnailSize, height: thumbnailSize)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:


UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return sectionInsets
}
}

PhotoCommentViewController.swift
import UIKit

class PhotoCommentViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!


@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var nameTextField: UITextField!

public var photoName: String!


public var photoIndex: Int!

override func viewDidLoad() {


super.viewDidLoad()

if let photoName = photoName {


imageView.image = UIImage(named: photoName)
}

let generalTapGesture = UITapGestureRecognizer(target: self, action: Selector.generalTap)


view.addGestureRecognizer(generalTapGesture)
let zoomTapGesture = UITapGestureRecognizer(target: self, action: Selector.zoomTap)
imageView.addGestureRecognizer(zoomTapGesture)

NotificationCenter.default.addObserver(
self,
selector: Selector.keyboardWillShowHandler,
name: NSNotification.Name.UIKeyboardWillShow,
object: nil

CSPIT 140
Ce373:Mobile Application Development 16CE068

)
NotificationCenter.default.addObserver(
self,
selector: Selector.keyboardWillHideHandler,
name: NSNotification.Name.UIKeyboardWillHide,
object: nil
)
}

deinit {
NotificationCenter.default.removeObserver(self)
}

fileprivate func adjustInsetForKeyboard(isShow: Bool, notification: Notification) {


guard let value = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue else {
return
}
let keyboardFrame = value.cgRectValue
let adjustmentHeight = (keyboardFrame.height + 20) * (isShow ? 1 : -1)
scrollView.contentInset.bottom += adjustmentHeight
scrollView.scrollIndicatorInsets.bottom += adjustmentHeight
}

@objc func dismissKeyboard() {


view.endEditing(true)
}

@objc func keyboardWillShow(notification: Notification) {


adjustInsetForKeyboard(isShow: true, notification: notification)
}

@objc func keyboardWillHide(notification: Notification) {


adjustInsetForKeyboard(isShow: false, notification: notification)
}

@objc func openZoomingController(sender: AnyObject) {


performSegue(withIdentifier: "zooming", sender: nil)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {


if let id = segue.identifier,
let zoomedPhotoViewController = segue.destination as? ZoomedPhotoViewController {
if id == "zooming" {
zoomedPhotoViewController.photoName = photoName
}
}
}
}

fileprivate extension Selector {


static let keyboardWillShowHandler =
#selector(PhotoCommentViewController.keyboardWillShow(notification:))
static let keyboardWillHideHandler =
#selector(PhotoCommentViewController.keyboardWillHide(notification:))
static let generalTap = #selector(PhotoCommentViewController.dismissKeyboard)
static let zoomTap = #selector(PhotoCommentViewController.openZoomingController(sender:))
}

ZoomedPhotoViewController.swift

CSPIT 141
Ce373:Mobile Application Development 16CE068

import UIKit

class ZoomedPhotoViewController: UIViewController {


@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var scrollView: UIScrollView!

@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!


@IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!
@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!

var photoName: String!

override func viewDidLoad() {


imageView.image = UIImage(named: photoName)
}

override func viewDidLayoutSubviews() {


super.viewDidLayoutSubviews()

updateMinZoomScale(forSize: view.bounds.size)
}

fileprivate func updateConstraints(forSize size: CGSize) {


let yOffset = max(0, (size.height - imageView.frame.height) / 2)
imageViewTopConstraint.constant = yOffset
imageViewBottomConstraint.constant = yOffset

let xOffset = max(0, (size.width - imageView.frame.width) / 2)


imageViewLeadingConstraint.constant = xOffset
imageViewTrailingConstraint.constant = xOffset

view.layoutIfNeeded()
}

fileprivate func updateMinZoomScale(forSize size: CGSize) {


let widthScale = size.width / imageView.bounds.width
let heightScale = size.height / imageView.bounds.height
let minScale = min(widthScale, heightScale)

scrollView.minimumZoomScale = minScale

/// set up the init zoom scale


scrollView.zoomScale = minScale
}
}

extension ZoomedPhotoViewController: UIScrollViewDelegate {

/// Tell the delegate that the imageView will be made smaller or bigger.
///
/// - Parameter scrollView: scrollView delegate to current view controller
/// - Returns: the view is zoomed in and out
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}

/// Called every time zoom in or out the scroll View


func scrollViewDidZoom(_ scrollView: UIScrollView) {

CSPIT 142
Ce373:Mobile Application Development 16CE068

updateConstraints(forSize: view.bounds.size)
}
}

ManagePageViewController.swift
import UIKit

class ManagePageViewController: UIPageViewController {

var photos = ["photo1", "photo2", "photo3", "photo4", "photo5"]


var currentIndex: Int!

override func viewDidLoad() {


super.viewDidLoad()

dataSource = self

if let viewController = viewPhotoCommentController(index: currentIndex ?? 0) {


let viewControllers = [viewController]
setViewControllers (
viewControllers,
direction: .forward,
animated: false,
completion: nil
)
}
}

fileprivate func viewPhotoCommentController(index: Int) -> PhotoCommentViewController? {


if let storyboard = storyboard,
let page = storyboard.instantiateViewController(withIdentifier: "PhotoCommentViewController")
as? PhotoCommentViewController {
page.photoName = photos[index]
page.photoIndex = index
return page
}
return nil
}
}

extension ManagePageViewController: UIPageViewControllerDataSource {


func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore
viewController: UIViewController) -> UIViewController? {

if let viewController = viewController as? PhotoCommentViewController {


guard let index = viewController.photoIndex, index != 0 else {
return nil
}
return viewPhotoCommentController(index: index - 1)
}
return nil
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter


viewController: UIViewController) -> UIViewController? {

if let viewController = viewController as? PhotoCommentViewController {


guard let index = viewController.photoIndex, index != photos.count - 1 else {

CSPIT 143
Ce373:Mobile Application Development 16CE068

return nil
}
return viewPhotoCommentController(index: index + 1)
}
return nil
}

func presentationCount(for pageViewController: UIPageViewController) -> Int {


return photos.count
}

func presentationIndex(for pageViewController: UIPageViewController) -> Int {


return currentIndex ?? 0
}
}
Output:

CSPIT 144
Ce373:Mobile Application Development 16CE068

 Animation
AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {


// Sent when the application is about to move from active to inactive state. This can occur for
certain types of temporary interruptions (such as an incoming phone call or SMS message) or when
the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame
rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// Use this method to release shared resources, save user data, invalidate timers, and store enough
application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of
applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the inactive state; here you can undo many
of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {


// Restart any tasks that were paused (or not yet started) while the application was inactive. If the
application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
}
}

ViewController.swift
import UIKit

let headerHeight = 50.0


let segueDetailIdentifier = "toAnimateDetail"
let duration = 1.5

class ViewController: UIViewController {

CSPIT 145
Ce373:Mobile Application Development 16CE068

// MARK: - IBOutlets
@IBOutlet weak var masterTableView: UITableView!

// MARK: - Variables
fileprivate let items = ["2-Color", "Simple 2D Rotation", "Multicolor", "Multi Point Position",
"BezierCurve Position",
"Color and Frame Change", "View Fade In", "Pop"]

// MARK: - Life Cycle


override func viewDidLoad() {
super.viewDidLoad()
}

override func viewWillAppear(_ animated: Bool) {


animateTable()
}

func animateTable() {
masterTableView.reloadData()

let cells = masterTableView.visibleCells


let tableHeight = masterTableView.bounds.size.height

// move all cells to the bottom of the screen


for cell in cells {
cell.transform = CGAffineTransform(translationX: 0, y: tableHeight)
}

// move all cells from bottom to the right place


var index = 0
for cell in cells {
UIView.animate(withDuration: duration, delay: 0.05 * Double(index), usingSpringWithDamping:
0.8, initialSpringVelocity: 0, options: [], animations: {
cell.transform = CGAffineTransform(translationX: 0, y: 0)
}, completion: nil)
index += 1
}
}

// MARK: - Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueDetailIdentifier {
let detailView = segue.destination as! DetailViewController
let indexPath = masterTableView.indexPathForSelectedRow

if let indexPath = indexPath {


detailView.barTitle = self.items[(indexPath as NSIndexPath).row]
}
}
}
}

// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat(headerHeight)
}

CSPIT 146
Ce373:Mobile Application Development 16CE068

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {


return "Basic Animations"
}
}

// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


return items.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)

cell.textLabel?.text = self.items[(indexPath as NSIndexPath).row]

return cell
}
}

DetailViewController.swift
import UIKit

class DetailViewController: UIViewController {

// MARK: - Variables
var barTitle = ""
var animateView: UIView!
fileprivate let duration = 2.0
fileprivate let delay = 0.2
fileprivate let scale = 1.2

// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupRect()
setupNavigationBar()
}

fileprivate func setupNavigationBar() {


navigationController?.navigationBar.topItem?.title = barTitle
}

fileprivate func setupRect() {


if barTitle == "BezierCurve Position" {
animateView = drawCircleView()

} else if barTitle == "View Fade In" {


animateView = UIImageView(image: UIImage(named: "whatsapp"))
animateView.frame = generalFrame
animateView.center = generalCenter
} else {

CSPIT 147
Ce373:Mobile Application Development 16CE068

animateView = drawRectView(UIColor.red, frame: generalFrame, center: generalCenter)


}
view.addSubview(animateView)
}

// MARK: - IBAction
@IBAction func didTapAnimate(_ sender: AnyObject) {
switch barTitle {
case "2-Color":
changeColor(UIColor.green)

case "Simple 2D Rotation":


rotateView(Double.pi)

case "Multicolor":
multiColor(UIColor.green, UIColor.blue)

case "Multi Point Position":


multiPosition(CGPoint(x: animateView.frame.origin.x, y: 100), CGPoint(x:
animateView.frame.origin.x, y: 350))

case "BezierCurve Position":


var controlPoint1 = self.animateView.center
controlPoint1.y -= 125.0
var controlPoint2 = controlPoint1
controlPoint2.x += 40.0
controlPoint2.y -= 125.0;
var endPoint = self.animateView.center;
endPoint.x += 75.0
curvePath(endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

case "Color and Frame Change":


let currentFrame = self.animateView.frame
let firstFrame = currentFrame.insetBy(dx: -30, dy: -50)
let secondFrame = firstFrame.insetBy(dx: 10, dy: 15)
let thirdFrame = secondFrame.insetBy(dx: -15, dy: -20)
colorFrameChange(firstFrame, secondFrame, thirdFrame, UIColor.orange, UIColor.yellow,
UIColor.green)

case "View Fade In":


viewFadeIn()

case "Pop":
Pop()

default:
let alert = makeAlert("Alert", message: "The animation not implemented yet", actionTitle: "OK")
self.present(alert, animated: true, completion: nil)
}
}

// MARK: - Private Methods for Animations


fileprivate func changeColor(_ color: UIColor) {
UIView.animate(withDuration: self.duration, animations: {
self.animateView.backgroundColor = color
}, completion: nil)
}

CSPIT 148
Ce373:Mobile Application Development 16CE068

fileprivate func multiColor(_ firstColor: UIColor, _ secondColor: UIColor) {


UIView.animate(withDuration: duration, animations: {
self.animateView.backgroundColor = firstColor
}, completion: { finished in
self.changeColor(secondColor)
})
}

fileprivate func multiPosition(_ firstPos: CGPoint, _ secondPos: CGPoint) {


func simplePosition(_ pos: CGPoint) {
UIView.animate(withDuration: self.duration, animations: {
self.animateView.frame.origin = pos
}, completion: nil)
}

UIView.animate(withDuration: self.duration, animations: {


self.animateView.frame.origin = firstPos
}, completion: { finished in
simplePosition(secondPos)
})
}

fileprivate func rotateView(_ angel: Double) {


UIView.animate(withDuration: duration, delay: delay, options: [.repeat], animations: {
self.animateView.transform = CGAffineTransform(rotationAngle: CGFloat(angel))
}, completion: nil)
}

fileprivate func colorFrameChange(_ firstFrame: CGRect, _ secondFrame: CGRect, _ thirdFrame:


CGRect,
_ firstColor: UIColor, _ secondColor: UIColor, _ thirdColor: UIColor) {
UIView.animate(withDuration: self.duration, animations: {
self.animateView.backgroundColor = firstColor
self.animateView.frame = firstFrame
}, completion: { finished in
UIView.animate(withDuration: self.duration, animations: {
self.animateView.backgroundColor = secondColor
self.animateView.frame = secondFrame
}, completion: { finished in
UIView.animate(withDuration: self.duration, animations: {
self.animateView.backgroundColor = thirdColor
self.animateView.frame = thirdFrame
}, completion: nil)
})
})
}

fileprivate func curvePath(_ endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) {


let path = UIBezierPath()
path.move(to: self.animateView.center)

path.addCurve(to: endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

// create a new CAKeyframeAnimation that animates the objects position


let anim = CAKeyframeAnimation(keyPath: "position")

// set the animations path to our bezier curve


anim.path = path.cgPath

CSPIT 149
Ce373:Mobile Application Development 16CE068

// set some more parameters for the animation


anim.duration = self.duration

// add the animation to the squares 'layer' property


self.animateView.layer.add(anim, forKey: "animate position along path")
self.animateView.center = endPoint
}

fileprivate func viewFadeIn() {


let secondView = UIImageView(image: UIImage(named: "facebook"))
secondView.frame = self.animateView.frame
secondView.alpha = 0.0

view.insertSubview(secondView, aboveSubview: self.animateView)

UIView.animate(withDuration: duration, delay: delay, options: .curveEaseOut, animations: {


secondView.alpha = 1.0
self.animateView.alpha = 0.0
}, completion: nil)
}

fileprivate func Pop() {


UIView.animate(withDuration: duration / 4,
animations: {
self.animateView.transform = CGAffineTransform(scaleX: CGFloat(self.scale), y:
CGFloat(self.scale))
}, completion: { finished in
UIView.animate(withDuration: self.duration / 4, animations: {
self.animateView.transform = CGAffineTransform.identity
})
})
}
}

Common.swift
import Foundation
import UIKit

let screenRect = UIScreen.main.bounds


let generalFrame = CGRect(x: 0, y: 0, width: screenRect.width / 2.0, height: screenRect.height / 4.0)
let generalCenter = CGPoint(x: screenRect.midX, y: screenRect.midY - 50)

func drawRectView(_ color: UIColor, frame: CGRect, center: CGPoint) -> UIView {
let view = UIView(frame: frame)
view.center = center
view.backgroundColor = color
return view
}

func drawCircleView() -> UIView {

CSPIT 150
Ce373:Mobile Application Development 16CE068

let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: screenRect.midY - 50), radius:


CGFloat(20), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)

let shapeLayer = CAShapeLayer()


shapeLayer.path = circlePath.cgPath

shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 3.0

let view = UIView()


view.layer.addSublayer(shapeLayer)

return view
}

func makeAlert(_ title: String, message: String, actionTitle: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle:
UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: actionTitle, style: UIAlertActionStyle.default, handler: nil))

return alert
}

Output:

CSPIT 151
Ce373:Mobile Application Development 16CE068

CSPIT 152
Ce373:Mobile Application Development 16CE068

PRACTICAL 14

14) Aim:PhotoTagger, Marslink, ceneDetector, Notification UI,Scale,


Weather Extention, HitList & Birthdays in iOS
 PhotoTagger
AppDelegate.shift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return true
}
}

PhotoColor.swift

import Foundation

struct PhotoColor {
var red: Int?
var green: Int?
var blue: Int?
var colorName: String?
}

TagsColorsTableViewController.shift
import UIKit

struct TagsColorTableData {
var label: String
var color: UIColor?
}

class TagsColorsTableViewController: UITableViewController {

// MARK: - Properties
var data: [TagsColorTableData]?

// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let data = data else {
return 0
}

return data.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let data = data else {
fatalError("Application error no cell data available")
}

let cellData = data[indexPath.row]

CSPIT 153
Ce373:Mobile Application Development 16CE068

let cell = tableView.dequeueReusableCell(withIdentifier: "TagOrColorCell", for: indexPath)


cell.textLabel?.text = cellData.label
return cell
}

// MARK: - UITableViewDelegate
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath:
IndexPath) {
guard let data = data else {
fatalError("Application error no cell data available")
}

let cellData = data[indexPath.row]


guard let color = cellData.color else {
cell.textLabel?.textColor = UIColor.black
cell.backgroundColor = UIColor.white
return
}

var red = CGFloat(0.0), green = CGFloat(0.0), blue = CGFloat(0.0), alpha = CGFloat(0.0)


color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let threshold = CGFloat(105)
let bgDelta = ((red * 0.299) + (green * 0.587) + (blue * 0.114));

let textColor = (255 - bgDelta < threshold) ? UIColor.black : UIColor.white;


cell.textLabel?.textColor = textColor
cell.backgroundColor = color
}
}

TagsColorsViewController
import UIKit

class TagsColorsViewController: UIViewController {

// MARK: - Properties
var tags: [String]?
var colors: [PhotoColor]?
var tableViewController: TagsColorsTableViewController!

// MARK: - IBOutlets
@IBOutlet var segmentedControl: UISegmentedControl!

// MARK: - View Life Cycle


override func viewDidLoad() {
super.viewDidLoad()
setupTableData()
}

// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DataTable" {
guard let controller = segue.destination as? TagsColorsTableViewController else {
fatalError("Storyboard mis-configuration. Controller is not of expected type TagsColorTableViewController")
}

tableViewController = controller
}
}

// MARK: - IBActions
@IBAction func tagsColorsSegmentedControlChanged(_ sender: UISegmentedControl) {
setupTableData()
}

CSPIT 154
Ce373:Mobile Application Development 16CE068

// MARK: - Public
func setupTableData() {
if segmentedControl.selectedSegmentIndex == 0 {

if let tags = tags {


tableViewController.data = tags.map {
TagsColorTableData(label: $0, color: nil)
}
} else {
tableViewController.data = [TagsColorTableData(label: "No tags were fetched.", color: nil)]
}
} else {
if let colors = colors {
tableViewController.data = colors.map({ (photoColor: PhotoColor) -> TagsColorTableData in
let uicolor = UIColor(red: CGFloat(photoColor.red!) / 255, green: CGFloat(photoColor.green!) / 255, blue:
CGFloat(photoColor.blue!) / 255, alpha: 1.0)
return TagsColorTableData(label: photoColor.colorName!, color: uicolor)
})
} else {
tableViewController.data = [TagsColorTableData(label: "No colors were fetched.", color: nil)]
}
}
tableViewController.tableView.reloadData()
}
}

ViewController

import UIKit
import Alamofire

let kAuthorization = ""

class ViewController: UIViewController {

// MARK: - IBOutlets
@IBOutlet var takePictureButton: UIButton!
@IBOutlet var imageView: UIImageView!
@IBOutlet var progressView: UIProgressView!
@IBOutlet var activityIndicatorView: UIActivityIndicatorView!

// MARK: - Properties
fileprivate var tags: [String]?
fileprivate var colors: [PhotoColor]?

// MARK: - View Life Cycle


override func viewDidLoad() {
super.viewDidLoad()

if !UIImagePickerController.isSourceTypeAvailable(.camera) {
takePictureButton.setTitle("Select Photo", for: UIControlState())
}
}

override func viewDidDisappear(_ animated: Bool) {


super.viewDidDisappear(animated)

imageView.image = nil
}

// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "ShowResults" {
guard let controller = segue.destination as? TagsColorsViewController else {

CSPIT 155
Ce373:Mobile Application Development 16CE068

fatalError("Storyboard mis-configuration. Controller is not of expected type TagsColorsViewController")


}

controller.tags = tags
controller.colors = colors
}
}

// MARK: - IBActions
@IBAction func takePicture(_ sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = false

if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.sourceType = UIImagePickerControllerSourceType.camera
} else {
picker.sourceType = .photoLibrary
picker.modalPresentationStyle = .fullScreen
}

present(picker, animated: true, completion: nil)


}
}

// MARK: - UIImagePickerControllerDelegate
extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String:


Any]) {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else {
print("Info did not have the required UIImage for the Original Image")
dismiss(animated: true)
return
}

imageView.image = image

takePictureButton.isHidden = true
progressView.progress = 0.0
progressView.isHidden = false
activityIndicatorView.startAnimating()

upload(
image: image,
progressCompletion: { [unowned self] percent in
self.progressView.setProgress(percent, animated: true)
},
completion: { [unowned self] tags, colors in
self.takePictureButton.isHidden = false
self.progressView.isHidden = true
self.activityIndicatorView.stopAnimating()

self.tags = tags
self.colors = colors

self.performSegue(withIdentifier: "ShowResults", sender: self)


})

dismiss(animated: true)
}
}

extension ViewController {
func upload(image: UIImage,
progressCompletion: @escaping (_ percent: Float) -> Void,

CSPIT 156
Ce373:Mobile Application Development 16CE068

completion: @escaping (_ tags: [String], _ colors: [PhotoColor]) -> Void) {


guard let imageData = UIImageJPEGRepresentation(image, 0.5) else {
print("Could not get JPEG representation of UIImage")
return
}

Alamofire.upload(
multipartFormData: { multipartFormData in
multipartFormData.append(imageData,
withName: "imagefile",
fileName: "image.jpg",
mimeType: "image/jpeg")
},
with: ImaggaRouter.content,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress { progress in
progressCompletion(Float(progress.fractionCompleted))
}
upload.validate()
upload.responseJSON { response in
guard response.result.isSuccess else {
print("Error while uploading file: \(response.result.error)")
completion([String](), [PhotoColor]())
return
}

guard let responseJSON = response.result.value as? [String: Any],


let uploadedFiles = responseJSON["uploaded"] as? [Any],
let firstFile = uploadedFiles.first as? [String: Any],
let firstFileID = firstFile["id"] as? String else {
print("Invalid information received from service")
completion([String](), [PhotoColor]())
return
}

print("Content uploaded with ID: \(firstFileID)")

self.downloadTags(contentID: firstFileID) { tags in


self.downloadColors(contentID: firstFileID) { colors in
completion(tags, colors)
}
}
}
case .failure(let encodingError):
print(encodingError)
}
}
)
}

func downloadTags(contentID: String, completion: @escaping ([String]) -> Void) {


Alamofire.request(ImaggaRouter.tags(contentID))
.responseJSON { response in

guard response.result.isSuccess else {


print("Error while fetching tags: \(response.result.error)")
completion([String]())
return
}

guard let responseJSON = response.result.value as? [String: Any],


let results = responseJSON["results"] as? [[String: Any]],
let firstObject = results.first,
let tagsAndConfidences = firstObject["tags"] as? [[String: Any]] else {

CSPIT 157
Ce373:Mobile Application Development 16CE068

print("Invalid tag information received from the service")


completion([String]())
return
}

let tags = tagsAndConfidences.flatMap({ dict in


return dict["tag"] as? String
})

completion(tags)
}
}

func downloadColors(contentID: String, completion: @escaping ([PhotoColor]) -> Void) {


Alamofire.request(ImaggaRouter.colors(contentID))
.responseJSON { response in

guard response.result.isSuccess else {


print("Error while fetching colors: \(response.result.error)")
completion([PhotoColor]())
return
}

guard let responseJSON = response.result.value as? [String: Any],


let results = responseJSON["results"] as? [[String: Any]],
let firstResult = results.first,
let info = firstResult["info"] as? [String: Any],
let imageColors = info["image_colors"] as? [[String: Any]] else {
print("Invalid color information received from service")
completion([PhotoColor]())
return
}

let photoColors = imageColors.flatMap({ (dict) -> PhotoColor? in


guard let r = dict["r"] as? String,
let g = dict["g"] as? String,
let b = dict["b"] as? String,
let closestPaletteColor = dict["closest_palette_color"] as? String else {
return nil
}

return PhotoColor(red: Int(r), green: Int(g), blue: Int(b), colorName: closestPaletteColor)


})

completion(photoColors)
}
}
}

ImaggaRouter.swift
import Foundation
import Alamofire

public enum ImaggaRouter: URLRequestConvertible {


static let baseURLPath = "http://api.imagga.com/v1"
static let authenticationToken = "Basic
YWNjXzM5MWRkMjU2NjE4ZWU0Mzo2NWFjODVmMzNhNWE1YmQ3YTMzZGJkNzFjNTlmYTM5Yw=="

case content
case tags(String)
case colors(String)

var method: HTTPMethod {


switch self {
case .content:

CSPIT 158
Ce373:Mobile Application Development 16CE068

return .post
case .tags, .colors:
return .get
}
}

var path: String {


switch self {
case .content:
return "/content"
case .tags:
return "/tagging"
case .colors:
return "/colors"
}
}
public func asURLRequest() throws -> URLRequest {
let parameters: [String: Any] = {
switch self {
case .tags(let contentID):
return ["content": contentID]
case .colors(let contentID):
return ["content": contentID, "extract_object_colors": 0]
default:
return [:]
}
}()
let url = try ImaggaRouter.baseURLPath.asURL()

var request = URLRequest(url: url.appendingPathComponent(path))


request.httpMethod = method.rawValue
request.setValue(ImaggaRouter.authenticationToken, forHTTPHeaderField: "Authorization")
request.timeoutInterval = TimeInterval(10 * 1000)

return try URLEncoding.default.encode(request, with: parameters)


}
}
Output:

CSPIT 159
Ce373:Mobile Application Development 16CE068

 MarsLink
AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = UIColor.black
let nav = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
nav.pushViewController(FeedViewController(), animated: false)
window?.rootViewController = nav
window?.makeKeyAndVisible()
return true
}

DateSortable.swift

import Foundation

protocol DateSortable {
var date: Date { get }
}

JournalEntry.swift
import Foundation

class JournalEntry: NSObject, DateSortable {

let date: Date


let text: String
let user: User

init(date: Date, text: String, user: User) {


self.date = date
self.text = text
self.user = user
}}
Message.swift
import UIKit

class Message: NSObject, DateSortable {

let date: Date


let text: String
let user: User

init(date: Date, text: String, user: User) {


self.date = date
self.text = text
self.user = user
}

NSObject+IGListDiffable.swift

CSPIT 160
Ce373:Mobile Application Development 16CE068

import Foundation
import IGListKit

// MARK: - IGListDiffable
extension NSObject: IGListDiffable {

public func diffIdentifier() -> NSObjectProtocol {


return self
}

public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {


return isEqual(object)
}
}

SolFormatter.swift
import Foundation

struct SolFormatter {

let landingDate: Date

init(landingDate: Date = Date(timeIntervalSinceNow: -31725960)) {


self.landingDate = landingDate
}

func sols(fromDate date: Date) -> Int {


let martianDay: TimeInterval = 1477 * 60 // 24h37m
let seconds = date.timeIntervalSince(landingDate)
return lround(seconds / martianDay)
}
}
User.swift
import Foundation

class User: NSObject {

let id: Int


let name: String

init(id: Int, name: String) {


self.id = id
self.name = name
}

Weather.swift
import UIKit
enum WeatherCondition: String {
case cloudy = "Cloudy"
case sunny = "Sunny"
case partlyCloudy = "Partly Cloudy"
case dustStorm = "Dust Storm"

var emoji: String {


switch self {
case .cloudy: return "☁"
case .sunny: return "☀"
case .partlyCloudy: return "⛅"
case .dustStorm: return "�"

CSPIT 161
Ce373:Mobile Application Development 16CE068

}
}
}

class Weather: NSObject {

let temperature: Int


let high: Int
let low: Int
let date: Date
let sunrise: String
let sunset: String
let condition: WeatherCondition

init(
temperature: Int,
high: Int,
low: Int,
date: Date,
sunrise: String,
sunset: String,
condition: WeatherCondition
){
self.temperature = temperature
self.high = high
self.low = low
self.date = date
self.sunrise = sunrise
self.sunset = sunset
self.condition = condition
}

JournalSectionController.swift
import IGListKit

class JournalSectionController: IGListSectionController {

fileprivate let solFormatter = SolFormatter()


fileprivate var entry: JournalEntry!

override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}

extension JournalSectionController: IGListSectionType {


func numberOfItems() -> Int {
return 2
}

func sizeForItem(at index: Int) -> CGSize {


guard let context = collectionContext, let entry = entry else {
return .zero
}

let width = context.containerSize.width

if index == 0 {
return CGSize(width: width, height: 30)
} else {
return JournalEntryCell.cellSize(width: width, text: entry.text)
}

CSPIT 162
Ce373:Mobile Application Development 16CE068

func cellForItem(at index: Int) -> UICollectionViewCell {


let cellClass: AnyClass = index == 0 ? JournalEntryDateCell.self : JournalEntryCell.self

let cell = collectionContext!.dequeueReusableCell(of: cellClass, for: self, at: index)

if let cell = cell as? JournalEntryDateCell {


cell.label.text = "SOL \(solFormatter.sols(fromDate: entry.date))"
} else if let cell = cell as? JournalEntryCell {
cell.label.text = entry.text
}

return cell
}

func didUpdate(to object: Any) {


entry = object as? JournalEntry
}

func didSelectItem(at index: Int) {}


}

MessageSectionController.swift
import UIKit
import IGListKit

class MessageSectionController: IGListSectionController {

fileprivate let solFormatter = SolFormatter()


fileprivate var message: Message!

override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}

extension MessageSectionController: IGListSectionType {


func numberOfItems() -> Int {
return 1
}

func sizeForItem(at index: Int) -> CGSize {


guard let context = collectionContext, let message = message else {
return .zero
}

let width = context.containerSize.width

return MessageCell.cellSize(width: width, text: message.text)


}

func cellForItem(at index: Int) -> UICollectionViewCell {


let cell = collectionContext!.dequeueReusableCell(of: MessageCell.self, for: self, at: index) as! MessageCell

cell.titleLabel.text = message.user.name.uppercased()
cell.messageLabel.text = message.text

return cell
}

func didUpdate(to object: Any) {


message = object as? Message
}

CSPIT 163
Ce373:Mobile Application Development 16CE068

func didSelectItem(at index: Int) {}


}

WeatherSectionController.swift
import UIKit
import IGListKit

class WeatherSectionController: IGListSectionController {

fileprivate var expanded = false


fileprivate var weather: Weather!

override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}

extension WeatherSectionController: IGListSectionType {


func numberOfItems() -> Int {
return expanded ? 5 : 1
}

func sizeForItem(at index: Int) -> CGSize {


guard let context = collectionContext else {
return .zero
}
let width = context.containerSize.width

if index == 0 {
return CGSize(width: width, height: 70)
} else {
return CGSize(width: width, height: 40)
}
}

func cellForItem(at index: Int) -> UICollectionViewCell {


let summaryCell = collectionContext!.dequeueReusableCell(of: WeatherSummaryCell.self, for: self, at: index)
as! WeatherSummaryCell
let detailCell = collectionContext!.dequeueReusableCell(of: WeatherDetailCell.self, for: self, at: index) as!
WeatherDetailCell
let detailInfo = [("Sunrise", weather.sunrise), ("Sunset", weather.sunset), ("High", "\(weather.high) C"), ("Low", "\
(weather.low) C")]

if index == 0 {
summaryCell.setExpanded(expanded)
return summaryCell
} else {
detailCell.titleLabel.text = detailInfo[index - 1].0
detailCell.detailLabel.text = detailInfo[index - 1].1
return detailCell
}
}

func didUpdate(to object: Any) {


weather = object as? Weather
}

func didSelectItem(at index: Int) {


expanded = !expanded
collectionContext?.reload(self)
}
}

CSPIT 164
Ce373:Mobile Application Development 16CE068

JournalLoader.swift
import Foundation

class JournalEntryLoader {

var entries = [JournalEntry]()

func loadLatest() {
let user = User(id: 1, name: "Mark Watney")
let entries = [
JournalEntry(
date: Date(timeIntervalSinceNow: -1727283),
text: "Ok I think I have this potato thing figured out. I'm using some of the leftover fuel from the landing
thruster and basically lighting it on fire. The hydrogen and oxygen combine to make water. If I throttle the reaction
I can let this run all day and generate enough water in the air to hydrate my potatos.\n\nThough, I'm basically
igniting jet fuel in my living room.",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -1382400),
text: "I blew up.\n\nMy potato hydration system was working perfectly, but I forgot to account for excess
oxygen from the reaction. I ended up with 30% pure oxygen in the HAB. Where I'm making mini explosions. Oh
did I mention I live here?\n\nI survived but the HAB is basically gone, along with all my potatos. The cold air
instantly froze the ones I have, so there's that at least.",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -823200),
text: "I figured out how to communicate with NASA! Years ago we sent a small probe called Pathfinder to
Mars to poke at the sand a bit. The little rover only lasted a couple months, but I found it! All I had to do was swap
the batteries and its as good as new.\n\nWith all this in place I can send pictures to NASA, maybe Johansen can
tell me how to hack this thing?",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -259200),
text: "Alright, its time for me to leave the HAB and make the several-thousand kilometer trek to the next
landing site. The MAV is already there, so I'm going to try to launch this thing and intercept with Hermes. Sounds
crazy, right?\n\nBut it's the last chance I've got.",
user: user
)
]
self.entries = entries
}
}

Pathfinder.swift
import Foundation

protocol PathfinderDelegate: class {


func pathfinderDidUpdateMessages(pathfinder: Pathfinder)
}

private func delay(time: Double = 1, execute work: @escaping @convention(block) () -> Swift.Void) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
work()
}
}

private func lewisMessage(text: String, interval: TimeInterval = 0) -> Message {


let user = User(id: 2, name: "cpt.lewis")
return Message(date: Date(timeIntervalSinceNow: interval), text: text, user: user)
}

CSPIT 165
Ce373:Mobile Application Development 16CE068

class Pathfinder {

weak var delegate: PathfinderDelegate?

var messages: [Message] = {


var arr = [Message]()
arr.append(lewisMessage(text: "Mark, are you receiving me?", interval: -803200))
arr.append(lewisMessage(text: "I think I left behind some ABBA, might help with the drive �", interval:
-259200))
return arr
}() {
didSet {
delegate?.pathfinderDidUpdateMessages(pathfinder: self)
}
}

func connect() {
delay(time: 2.3) {
self.messages.append(lewisMessage(text: "Liftoff in 3..."))
delay {
self.messages.append(lewisMessage(text: "2..."))
delay {
self.messages.append(lewisMessage(text: "1..."))
}
}
}
}
}

TextSize.swift
import UIKit

public struct TextSize {

fileprivate struct CacheEntry: Hashable {


let text: String
let font: UIFont
let width: CGFloat
let insets: UIEdgeInsets

fileprivate var hashValue: Int {


return text.hashValue ^ Int(width) ^ Int(insets.top) ^ Int(insets.left) ^ Int(insets.bottom) ^ Int(insets.right)
}
}

fileprivate static var cache = [CacheEntry: CGRect]() {


didSet {
assert(Thread.isMainThread)
}
}

public static func size(_ text: String, font: UIFont, width: CGFloat, insets: UIEdgeInsets = UIEdgeInsets.zero) ->
CGRect {
let key = CacheEntry(text: text, font: font, width: width, insets: insets)
if let hit = cache[key] {
return hit
}

let constrainedSize = CGSize(width: width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)


let attributes = [ NSAttributedStringKey.font: font ]
let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
var bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes,
context: nil)

CSPIT 166
Ce373:Mobile Application Development 16CE068

bounds.size.width = width
bounds.size.height = ceil(bounds.height + insets.top + insets.bottom)
cache[key] = bounds
return bounds
}
}

private func ==(lhs: TextSize.CacheEntry, rhs: TextSize.CacheEntry) -> Bool {


return lhs.width == rhs.width && lhs.insets == rhs.insets && lhs.text == rhs.text
}

Theme.swift
import UIKit

extension UIColor {
// https://github.com/yeahdongcn/UIColor-Hex-Swift/blob/master/HEXColor/UIColorExtension.swift
public convenience init(hex6: UInt32, alpha: CGFloat = 1) {
let divisor = CGFloat(255)
let red = CGFloat((hex6 & 0xFF0000) >> 16) / divisor
let green = CGFloat((hex6 & 0x00FF00) >> 8) / divisor
let blue = CGFloat( hex6 & 0x0000FF ) / divisor
self.init(red: red, green: green, blue: blue, alpha: alpha)
}
}

let CommonInsets = UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 15)

func AppFont(size: CGFloat = 18) -> UIFont {


return UIFont(name: "OCRAStd", size: size)!
}

WxScanner.swift
import Foundation

class WxScanner {

let currentWeather = Weather(


temperature: 6,
high: 13,
low: -69,
date: Date(),
sunrise: "05:42",
sunset: "17:58",
condition: .dustStorm
)

ClassicFeedViewController.swift
import UIKit

class ClassicFeedViewController: UIViewController {

let loader = JournalEntryLoader()


let solFormatter = SolFormatter()

let collectionView: UICollectionView = {


let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)

CSPIT 167
Ce373:Mobile Application Development 16CE068

let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)


view.backgroundColor = UIColor.black
view.alwaysBounceVertical = true
return view
}()

override func viewDidLoad() {


super.viewDidLoad()

collectionView.register(JournalEntryCell.self, forCellWithReuseIdentifier: "JournalEntryCell")


collectionView.register(JournalEntryDateCell.self, forCellWithReuseIdentifier: "JournalEntryDateCell")
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)

loader.loadLatest()
}

override func viewDidLayoutSubviews() {


super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
}

//MARK: UICollectionViewDataSource
extension ClassicFeedViewController: UICollectionViewDataSource {

func numberOfSections(in collectionView: UICollectionView) -> Int {


return loader.entries.count
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {


return 2
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) ->


UICollectionViewCell {
let identifier = (indexPath as NSIndexPath).item == 0 ? "JournalEntryDateCell" : "JournalEntryCell"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
let entry = loader.entries[(indexPath as NSIndexPath).section]
if let cell = cell as? JournalEntryDateCell {
cell.label.text = "SOL \(solFormatter.sols(fromDate: entry.date))"
} else if let cell = cell as? JournalEntryCell {
cell.label.text = entry.text
}
return cell
}
}

//MARK: UICollectionViewDelegateFlowLayout
extension ClassicFeedViewController: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,


sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.bounds.width
if indexPath.item == 0 {
return CGSize(width: width, height: 30)
} else {
let entry = loader.entries[indexPath.section]
return JournalEntryCell.cellSize(width: width, text: entry.text)
}
}
}

FeedViewController.swift

CSPIT 168
Ce373:Mobile Application Development 16CE068

import UIKit
import IGListKit

class FeedViewController: UIViewController {

fileprivate let loader = JournalEntryLoader()


fileprivate let pathfinder = Pathfinder()
fileprivate let wxScanner = WxScanner()

fileprivate let collectionView: IGListCollectionView = {


let view = IGListCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
view.backgroundColor = UIColor.black
return view
}()

fileprivate lazy var adapter: IGListAdapter = {


return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()

override func viewDidLoad() {


super.viewDidLoad()

func setupUI() {
view.addSubview(collectionView)
}

func setupDateSource() {
loader.loadLatest()

adapter.collectionView = collectionView
adapter.dataSource = self

pathfinder.delegate = self
pathfinder.connect()
}

setupUI()
setupDateSource()
}

override func viewDidLayoutSubviews() {


super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
}

extension FeedViewController: IGListAdapterDataSource {


/// Populate data to collection view.
///
/// - Parameter listAdapter: The adapter for IGList.
/// - Returns: Data objects to show on collection view.
func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
var items: [IGListDiffable] = [wxScanner.currentWeather]
items += loader.entries as [IGListDiffable]
items += pathfinder.messages as [IGListDiffable]

return items.sorted(by: { (left: Any, right: Any) -> Bool in


if let left = left as? DateSortable, let right = right as? DateSortable {
return left.date > right.date
}
return false
})
}

/// Asks the section controller for each data object.


///

CSPIT 169
Ce373:Mobile Application Development 16CE068

/// - Parameters:
/// - listAdapter: The adapter for IGList.
/// - object: The data object.
/// - Returns: The secion controller for data object.
func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
if object is Message {
return MessageSectionController()
} else if object is Weather {
return WeatherSectionController()
} else {
return JournalSectionController()
}

/// Requests a view when list is empty.


///
/// - Parameter listAdapter: The adapter for IGList.
/// - Returns: The view shown when list is empty.
func emptyView(for listAdapter: IGListAdapter) -> UIView? {
return nil
}
}

extension FeedViewController: PathfinderDelegate {


func pathfinderDidUpdateMessages(pathfinder: Pathfinder) {
adapter.performUpdates(animated: true)
}
}
CustomNavigationBar.swift
import UIKit

class CustomNavigationBar: UINavigationBar {

let titleLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.text = "MARSLINK"
label.font = AppFont()
label.textAlignment = .center
label.textColor = UIColor.white
return label
}()

let statusLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.text = "RECEIVING"
label.font = AppFont(size: 13)
label.textAlignment = .center
label.textColor = UIColor(hex6: 0x42c84b)
label.sizeToFit()
return label
}()

let statusIndicator: CAShapeLayer = {


let layer = CAShapeLayer()
layer.strokeColor = UIColor.white.cgColor
layer.lineWidth = 1
layer.fillColor = UIColor.black.cgColor
let size: CGFloat = 8
let frame = CGRect(x: 0, y: 0, width: size, height: size)
layer.path = UIBezierPath(roundedRect: frame, cornerRadius: size/2).cgPath
layer.frame = frame
return layer
}()

CSPIT 170
Ce373:Mobile Application Development 16CE068

let highlightLayer: CAShapeLayer = {


let layer = CAShapeLayer()
layer.fillColor = UIColor(hex6: 0x76879D).cgColor
return layer
}()

override init(frame: CGRect) {


super.init(frame: frame)
layer.addSublayer(highlightLayer)
layer.addSublayer(statusIndicator)
addSubview(titleLabel)
addSubview(statusLabel)
barTintColor = UIColor.black
updateStatus()
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
let titleWidth: CGFloat = 130
let borderHeight: CGFloat = 4

let path = UIBezierPath()


path.move(to: .zero)
path.addLine(to: CGPoint(x: titleWidth, y: 0))
path.addLine(to: CGPoint(x: titleWidth, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
path.addLine(to: CGPoint(x: 0, y: bounds.height))
path.close()
highlightLayer.path = path.cgPath

titleLabel.frame = CGRect(x: 0, y: 0, width: titleWidth, height: bounds.height)


statusLabel.frame = CGRect(
x: bounds.width - statusLabel.bounds.width - CommonInsets.right,
y: bounds.height - borderHeight - statusLabel.bounds.height - 6,
width: statusLabel.bounds.width,
height: statusLabel.bounds.height
)
statusIndicator.position = CGPoint(x: statusLabel.center.x - 50, y: statusLabel.center.y - 1)
}

var statusOn = false


func updateStatus() {
statusOn = !statusOn
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
statusIndicator.fillColor = (statusOn ? UIColor.white : UIColor.black).cgColor
CATransaction.commit()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6) {
self.updateStatus()
}
}
}

JournalEntryCell.swift
import UIKit

class JournalEntryCell: UICollectionViewCell {

static let font = AppFont()

CSPIT 171
Ce373:Mobile Application Development 16CE068

static let inset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)

static func cellSize(width: CGFloat, text: String) -> CGSize {


return TextSize.size(text, font: JournalEntryCell.font, width: width, insets: JournalEntryCell.inset).size
}

let label: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.numberOfLines = 0
label.font = JournalEntryCell.font
label.textColor = UIColor.white
return label
}()

override init(frame: CGRect) {


super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
label.frame = UIEdgeInsetsInsetRect(bounds, JournalEntryCell.inset)
}

}
JournalEntryDateCell.swift
import UIKit

class JournalEntryDateCell: UICollectionViewCell {


let label: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.clear
label.font = AppFont(size: 14)
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()

override init(frame: CGRect) {


super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
let padding = CommonInsets
label.frame = UIEdgeInsetsInsetRect(bounds, UIEdgeInsetsMake(0, padding.left, 0, padding.right))
}

MessageCell.swift

CSPIT 172
Ce373:Mobile Application Development 16CE068

import UIKit

class MessageCell: UICollectionViewCell {

static let titleHeight: CGFloat = 30


static let font = AppFont()

static func cellSize(width: CGFloat, text: String) -> CGSize {


let labelBounds = TextSize.size(text, font: MessageCell.font, width: width, insets: CommonInsets)
return CGSize(width: width, height: labelBounds.height + MessageCell.titleHeight)
}

let messageLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.numberOfLines = 0
label.font = MessageCell.font
label.textColor = UIColor.white
return label
}()

let titleLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.font = AppFont(size: 14)
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()

let statusLabel: UILabel = {


let label = UILabel()
label.layer.borderColor = UIColor(hex6: 0x76879d).cgColor
label.layer.borderWidth = 1
label.backgroundColor = UIColor.clear
label.font = AppFont(size: 8)
label.textColor = UIColor(hex6: 0x76879d)
label.textAlignment = .center
label.text = "NEW MESSAGE"
return label
}()

override init(frame: CGRect) {


super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(messageLabel)
contentView.addSubview(titleLabel)
contentView.addSubview(statusLabel)
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
titleLabel.frame = CGRect(x: CommonInsets.left, y: 0, width: bounds.width - CommonInsets.left -
CommonInsets.right, height: MessageCell.titleHeight)
statusLabel.frame = CGRect(x: bounds.width - 80, y: 4, width: 70, height: 18)
let messageFrame = CGRect(x: 0, y: titleLabel.frame.maxY, width: bounds.width, height: bounds.height -
MessageCell.titleHeight)
messageLabel.frame = UIEdgeInsetsInsetRect(messageFrame, CommonInsets)
}
}
WeatherDetailCell.swift
import UIKit

CSPIT 173
Ce373:Mobile Application Development 16CE068

class WeatherDetailCell: UICollectionViewCell {

let titleLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.font = AppFont()
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()

let detailLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.font = AppFont()
label.textColor = UIColor(hex6: 0x42c84b)
label.textAlignment = .right
return label
}()

override init(frame: CGRect) {


super.init(frame: frame)
contentView.addSubview(titleLabel)
contentView.addSubview(detailLabel)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
let insetBounds = UIEdgeInsetsInsetRect(bounds, CommonInsets)
titleLabel.frame = insetBounds
detailLabel.frame = insetBounds
}

WeatherSummaryCell.swift
import UIKit

class WeatherSummaryCell: UICollectionViewCell {

private let expandLabel: UILabel = {


let label = UILabel()
label.backgroundColor = UIColor.clear
label.font = AppFont(size: 30)
label.textColor = UIColor(hex6: 0x44758b)
label.textAlignment = .center
label.text = ">>"
label.sizeToFit()
return label
}()

let titleLabel: UILabel = {

CSPIT 174
Ce373:Mobile Application Development 16CE068

let label = UILabel()


label.backgroundColor = .clear
label.numberOfLines = 0

let paragraphStyle = NSMutableParagraphStyle()


paragraphStyle.paragraphSpacing = 4
let subtitleAttributes = [
NSAttributedStringKey.font: AppFont(size: 14),
NSAttributedStringKey.foregroundColor: UIColor(hex6: 0x42c84b),
NSAttributedStringKey.paragraphStyle: paragraphStyle
]
let titleAttributes = [
NSAttributedStringKey.font: AppFont(size: 24),
NSAttributedStringKey.foregroundColor: UIColor.white
]
let attributedText = NSMutableAttributedString(string: "LATEST\n", attributes: subtitleAttributes)
attributedText.append(NSAttributedString(string: "WEATHER", attributes: titleAttributes))
label.attributedText = attributedText
label.sizeToFit()

return label
}()

func setExpanded(_ expanded: Bool) {


self.expandLabel.transform = expanded ? CGAffineTransform(rotationAngle: CGFloat.pi / 2) :
CGAffineTransform.identity
}

override init(frame: CGRect) {


super.init(frame: frame)
contentView.addSubview(expandLabel)
contentView.addSubview(titleLabel)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
}

required init?(coder aDecoder: NSCoder) {


fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {


super.layoutSubviews()
let insets = CommonInsets
titleLabel.frame = CGRect(x: insets.left, y: 0, width: titleLabel.bounds.width, height: bounds.height)
expandLabel.center = CGPoint(x: bounds.width - expandLabel.bounds.width/2 - insets.right, y:
bounds.height/2)
}

}
Output:

CSPIT 175
Ce373:Mobile Application Development 16CE068

 Scene detector
AppDelegate.shift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication,


didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return true
}
}

ViewController.shift
import UIKit
import CoreML
import Vision
class ViewController: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var scene: UIImageView!
@IBOutlet weak var answerLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard let image = UIImage(named: "train_night") else {
fatalError("no starting image")

CSPIT 176
Ce373:Mobile Application Development 16CE068

}
scene.image = image
}
}
extension ViewController {
@IBAction func pickImage(_ sender: Any) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = .savedPhotosAlbum
present(pickerController, animated: true)
}
}
extension ViewController: UIImagePickerControllerDelegate {

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String :


Any]) {
dismiss(animated: true)

guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else {


fatalError("couldn't load image from Photos")
}

scene.image = image

guard let ciImage = CIImage(image: image) else {


fatalError("couldn't convert UIImage to CIImage")
}
detectScene(image: ciImage)
}
}
// MARK: - UINavigationControllerDelegate
extension ViewController: UINavigationControllerDelegate {
}

// MARK: - Private functions


extension ViewController {
func detectScene(image: CIImage) {
answerLabel.text = "detecting scene..."

// Load the ML model through its generated class


guard let model = try? VNCoreMLModel(for: GoogLeNetPlaces().model) else {
fatalError("can't load Places ML model")
}

// Define a Vision request service with the ML model


let request = VNCoreMLRequest(model: model) { [weak self] request, error in
guard let results = request.results,
let topResult = results.first as? VNClassificationObservation else {
fatalError("unexpected result type from VNCoreMLRequest")
}

// Update UI on main queue


let article = (["a", "e", "i", "o", "u"].contains(topResult.identifier.first!)) ? "an" : "a"

DispatchQueue.main.async { [weak self] in


self?.answerLabel.text = "\(Int(topResult.confidence * 100))% it's \(article) \(topResult.identifier)"
}
}

// Create a request handler with the image provided


let handler = VNImageRequestHandler(ciImage: image)

// Perform the request service with the request handler


DispatchQueue.global(qos: .userInteractive).async {
do {
try handler.perform([request])

CSPIT 177
Ce373:Mobile Application Development 16CE068

} catch {
print(error)
}
}
}
}
Output:

 NotificationUI
AppDelegate.swift
import UIKit
import UserNotifications

@UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate {


var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {

/// Request local notification authorizations.


UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { accepted, error in
if !accepted {
print("Notification access denied.")
}
}

/// Render actions for notification.


let action = UNNotificationAction(identifier: "remindLater", title: "Remind me later", options: [])
let category = UNNotificationCategory(identifier: "normal", actions: [action], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])

return true
}

/// Create a local notification at specific date.


///
/// - Parameter date: Time to trigger notification.
func scheduleNotification(at date: Date) {
UNUserNotificationCenter.current().delegate = self

CSPIT 178
Ce373:Mobile Application Development 16CE068

/// Create date component from date.


let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents.init(calendar: calendar, timeZone: .current, month:
components.month, day: components.day, hour: components.hour, minute: components.minute)

/// Create trigger and content.


let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Coding Reminder"
content.body = "Ready to code? Let us do some Swift!"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "normal"

/// Add a image as attachment.


if let path = Bundle.main.path(forResource: "Swift", ofType: "png") {
let url = URL(fileURLWithPath: path)

do {
let attachment = try UNNotificationAttachment(identifier: "Swift", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("The attachment was not loaded.")
}
}

/// Make a notification request.


let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger)

/// Remove pending notifications to avoid duplicates.


UNUserNotificationCenter.current().removeAllPendingNotificationRequests()

/// Provide request to notification center.


UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print("Error: " + error.localizedDescription)
}
}
}
}

extension AppDelegate: UNUserNotificationCenterDelegate {


func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response:
UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == "remindLater" {
let newDate = Date(timeInterval: 60, since: Date())
scheduleNotification(at: newDate)
}
}
}

ViewController.swift

import UIKit

class ViewController: UIViewController {


@IBAction func datePickerDidSelectNewDate(_ sender: UIDatePicker) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.scheduleNotification(at: sender.date)
}
}
}

CSPIT 179
Ce373:Mobile Application Development 16CE068

Output:

 Scale
AppDelegate.swift
import UIKit
enum Shortcut: String {
case openBlue = "OpenBlue"
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {

print("didFinishLaunchingWithOptions called")
var isLaunchedFromQuickAction = false

if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsKey.shortcutItem] as?


UIApplicationShortcutItem {
isLaunchedFromQuickAction = true
let _ = handleQuickAction(shortcutItem)
} else {
self.window?.backgroundColor = UIColor.white
}

return !isLaunchedFromQuickAction

CSPIT 180
Ce373:Mobile Application Development 16CE068

func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {

return true
}

func handleQuickAction(_ shortcutItem: UIApplicationShortcutItem) -> Bool {


var quickActionHandled = false
let type = shortcutItem.type.components(separatedBy: ".").last!

if let shortcutType = Shortcut.init(rawValue: type) {


switch shortcutType {
case .openBlue:
self.window?.backgroundColor = UIColor(red: 151.0/255.0, green: 187.0/255.0, blue: 255.0/255.0, alpha:
1.0)
quickActionHandled = true
}
}
return quickActionHandled }
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var forceLabel: UILabel!
// MARK: - init
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning() }
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// check only one object in touches
guard let touch = touches.first else {
return
}
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
if touch.force >= touch.maximumPossibleForce {
forceLabel.text = "385+ g"
} else {
let force = touch.force / touch.maximumPossibleForce
let grams = Int(force * 385)
forceLabel.text = "\(grams) g"
} } }
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
forceLabel.text = "0 g"
}}
Output:

CSPIT 181
Ce373:Mobile Application Development 16CE068

 WeatherExtension
AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.

return true
}

func applicationWillResignActive(_ application: UIApplication) {


// Sent when the application is about to move from active to inactive state. This can occur for certain types of
temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application
and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates.
Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// Use this method to release shared resources, save user data, invalidate timers, and store enough
application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of
applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the inactive state; here you can undo many of the
changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {

CSPIT 182
Ce373:Mobile Application Development 16CE068

// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application
was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {


// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
}

}
ViewController.swift
import UIKit
import WeatherKit

class ViewController: UIViewController {


@IBOutlet weak var cityLabel:UILabel!
@IBOutlet weak var countryLabel:UILabel!
@IBOutlet weak var weatherLabel:UILabel!
@IBOutlet weak var temperatureLabel:UILabel!

var city = "San Francisco"


var country = "U.S."

override func viewDidLoad() {


super.viewDidLoad()

weatherLabel.text = ""
temperatureLabel.text = ""

displayCurrentWeather()
}

override func didReceiveMemoryWarning() {


super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func displayCurrentWeather() {
// Update location
cityLabel.text = city
countryLabel.text = country

// Invoke weather service to get the weather data


WeatherService.sharedWeatherService().getCurrentWeather(city + "," + country, completion: { (data) -> () in
OperationQueue.main.addOperation({ () -> Void in
if let weatherData = data {
self.weatherLabel.text = weatherData.weather.capitalized
self.temperatureLabel.text = String(format: "%d", weatherData.temperature) + "\u{00B0}"
}
})
})
}

@IBAction func unwindToHome(_ segue: UIStoryboardSegue) {


}

@IBAction func updateWeatherInfo(_ segue: UIStoryboardSegue) {


let sourceViewController = segue.source as! LocationTableViewController
var selectedLocation = sourceViewController.selectedLocation.characters.split { $0 == "," }.map
{ String($0) }
city = selectedLocation[0]
country = selectedLocation[1].trimmingCharacters(in: CharacterSet.whitespaces)

displayCurrentWeather()

CSPIT 183
Ce373:Mobile Application Development 16CE068

// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if segue.identifier == "showLocations" {
let destinationController = segue.destination as! UINavigationController
let locationTableViewController = destinationController.viewControllers[0] as! LocationTableViewController
locationTableViewController.selectedLocation = "\(city), \(country)"
}
}

Output:

 Hitlist
AppDelegate.swift
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {

CSPIT 184
Ce373:Mobile Application Development 16CE068

// Sent when the application is about to move from active to inactive state. This can occur for certain types of
temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application
and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games
should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// Use this method to release shared resources, save user data, invalidate timers, and store enough
application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate:
when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the inactive state; here you can undo many of the
changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {


// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application
was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {


// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}

// MARK: - Core Data stack

lazy var applicationDocumentsDirectory: URL = {


// The directory the application uses to store the Core Data store file. This code uses a directory named
"com.yigu.HitList" in the application's documents Application Support directory.
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()

lazy var managedObjectModel: NSManagedObjectModel = {


// The managed object model for the application. This property is not optional. It is a fatal error for the
application not to be able to find and load its model.
let modelURL = Bundle.main.url(forResource: "HitList", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {


// The persistent store coordinator for the application. This implementation creates and returns a coordinator,
having added the store for the application to it. This property is optional since there are legitimate error conditions
that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?

dict[NSUnderlyingErrorKey] = error as NSError


let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.

CSPIT 185
Ce373:Mobile Application Development 16CE068

// abort() causes the application to generate a crash log and terminate. You should not use this function in
a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}

return coordinator
}()

lazy var managedObjectContext: NSManagedObjectContext = {


// Returns the managed object context for the application (which is already bound to the persistent store
coordinator for the application.) This property is optional since there are legitimate error conditions that could
cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()

// MARK: - Core Data Saving support

func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function
in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}

ViewController.swift
import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!

var people: [NSManagedObject] = []

// MARK - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
self.setupNavigationTitle()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
self.fetchCoreData(managedContext)
}

func setupNavigationTitle() {
title = "The List"
}

func fetchCoreData(_ managedContext: NSManagedObjectContext) {


let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")

CSPIT 186
Ce373:Mobile Application Development 16CE068

do {
let results =
try managedContext.fetch(fetchRequest)
people = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}

// MARK - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let identifier: String = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)

let person = people[indexPath.row]


cell.textLabel!.text = person.value(forKey: "name") as? String

return cell
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt


indexPath: IndexPath) {
switch editingStyle {
case .delete:
// remove the deleted item from the model
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
managedContext.delete(people[indexPath.row] as NSManagedObject)
do {
try managedContext.save()
people.remove(at: indexPath.row)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}

//tableView.reloadData()
// remove the deleted item from the `UITableView`
self.tableView.deleteRows(at: [indexPath], with: .fade)
default:
return

}
}

// MARK - IBActions
@IBAction func addName(_ sender: AnyObject) {
let alert = UIAlertController(title: "New Name", message: "Add a new name", preferredStyle: .alert)

let saveAction = UIAlertAction(title: "Save", style: .default, handler: {(action: UIAlertAction) -> Void in
let textField = alert.textFields!.first
self.saveName(textField!.text!)
self.tableView.reloadData()
})

let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: {(action: UIAlertAction) -> Void in
})

alert.addTextField {
(textField: UITextField) -> Void in
}

CSPIT 187
Ce373:Mobile Application Development 16CE068

alert.addAction(saveAction)
alert.addAction(cancelAction)

present(alert, animated: true, completion: nil)


}

// MARK - CoreData func


func saveName(_ name: String) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext

let entity = NSEntityDescription.entity(forEntityName: "Person",


in:managedContext)
let person = NSManagedObject(entity: entity!,
insertInto: managedContext)

person.setValue(name, forKey: "name")

do {
try managedContext.save()
people.append(person)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}

Output:

 birthdays
AppDelegate.swift
import UIKit
import Contacts

CSPIT 188
Ce373:Mobile Application Development 16CE068

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

static var appDelegate: AppDelegate { return UIApplication.shared.delegate as! AppDelegate }

var window: UIWindow?


var contactStore = CNContactStore()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:


[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func requestForAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void) {
let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts)

switch authorizationStatus {
case .authorized:
completionHandler(true)
case .denied, .notDetermined:
self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) ->
Void in
if access {
completionHandler(access)
} else {
if authorizationStatus == CNAuthorizationStatus.denied {
DispatchQueue.main.async {
let message = "\(accessError!.localizedDescription)\n\nPlease allow the app to access your contacts
through the Settings."
Helper.show(message: message)
}
}
}
})
default:
completionHandler(false)
}
}

}
Helper.swift
import Foundation
import UIKit

class Helper {
static func show(message: String) {
let alertController = UIAlertController(title: "Birthdays", message: message, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "OK", style: .default, handler: nil)

alertController.addAction(dismissAction)

let pushedViewControllers = (UIApplication.shared.keyWindow?.rootViewController as!


UINavigationController).viewControllers
let presentedViewController = pushedViewControllers.last

presentedViewController?.present(alertController, animated: true, completion: nil)


}
}

extension DateComponents {
var asString: String? {
if let date = Calendar.current.date(from: self) {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current

CSPIT 189
Ce373:Mobile Application Development 16CE068

dateFormatter.dateStyle = .medium
let dateString = dateFormatter.string(from: date)

return dateString
}

return nil
}
}

ViewController.swift
import UIKit
import Contacts
import ContactsUI

class ViewController: UIViewController {

@IBOutlet weak var tblContacts: UITableView!

var contacts = [CNContact]()

override func viewDidLoad() {


super.viewDidLoad()

navigationController?.navigationBar.tintColor = UIColor(red: 241.0/255.0, green: 107.0/255.0, blue: 38.0/255.0,


alpha: 1.0)

configureTableView()
}

// MARK: IBAction functions


@IBAction func addContact(_ sender: AnyObject) {
performSegue(withIdentifier: "idSegueAddContact", sender: self)
}

// MARK: Custom functions


func configureTableView() {
tblContacts.delegate = self
tblContacts.dataSource = self
tblContacts.register(UINib(nibName: "ContactBirthdayCell", bundle: nil), forCellReuseIdentifier:
"idCellContactBirthday")
}

/// Set ViewController class as the delegate of the AddContactViewControllerDelegate protocol


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "idSegueAddContact" {
let addContactViewController = segue.destination as! AddContactViewController
addContactViewController.delegate = self
}
}
}
}

// MARK: UITableView Delegate and Datasource functions


extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


return contacts.count
}

CSPIT 190
Ce373:Mobile Application Development 16CE068

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


let cell = tableView.dequeueReusableCell(withIdentifier: "idCellContactBirthday") as! ContactBirthdayCell

let currentContact = contacts[indexPath.row]

cell.lblFullname.text = CNContactFormatter.string(from: currentContact, style: .fullName)

if !currentContact.isKeyAvailable(CNContactBirthdayKey) || !
currentContact.isKeyAvailable(CNContactImageDataKey) || !
currentContact.isKeyAvailable(CNContactEmailAddressesKey) {
refetch(contact: currentContact, atIndexPath: indexPath)
} else {
// Set the birthday info.
if let birthday = currentContact.birthday {
cell.lblBirthday.text = birthday.asString
}
else {
cell.lblBirthday.text = "Not available birthday data"
}

// Set the contact image.


if let imageData = currentContact.imageData {
cell.imgContactImage.image = UIImage(data: imageData)
}

// Set the contact's home email address.


var homeEmailAddress: String!
for emailAddress in currentContact.emailAddresses {
if emailAddress.label == CNLabelHome {
homeEmailAddress = emailAddress.value as String
break
}
}
if let homeEmailAddress = homeEmailAddress {
cell.lblEmail.text = homeEmailAddress
} else {
cell.lblEmail.text = "Not available home email"
}
}

return cell

fileprivate func refetch(contact: CNContact, atIndexPath indexPath: IndexPath) {


AppDelegate.appDelegate.requestForAccess { (accessGranted) -> Void in
if accessGranted {
let keys = [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName),
CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [Any]

do {
let contactRefetched = try AppDelegate.appDelegate.contactStore.unifiedContact(withIdentifier:
contact.identifier, keysToFetch: keys as! [CNKeyDescriptor])
self.contacts[indexPath.row] = contactRefetched

DispatchQueue.main.async {
self.tblContacts.reloadRows(at: [indexPath], with: .automatic)
}
}
catch {
print("Unable to refetch the contact: \(contact)", separator: "", terminator: "\n")
}
}
}

CSPIT 191
Ce373:Mobile Application Development 16CE068

}
}

extension ViewController: UITableViewDelegate {


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {


return true
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt


indexPath: IndexPath) {
if editingStyle == .delete {
contacts.remove(at: indexPath.row)
tblContacts.reloadData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedContact = contacts[indexPath.row]
let keys = [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName),
CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [Any]
if selectedContact.areKeysAvailable([CNContactViewController.descriptorForRequiredKeys()]) {
let contactViewController = CNContactViewController(for: selectedContact)
contactViewController.contactStore = AppDelegate.appDelegate.contactStore
contactViewController.displayedPropertyKeys = keys
navigationController?.pushViewController(contactViewController, animated: true)
}
else {
AppDelegate.appDelegate.requestForAccess(completionHandler: { (accessGranted) -> Void in
if accessGranted {
do {
let contactRefetched = try AppDelegate.appDelegate.contactStore.unifiedContact(withIdentifier:
selectedContact.identifier, keysToFetch: [CNContactViewController.descriptorForRequiredKeys()])
DispatchQueue.main.async {
let contactViewController = CNContactViewController(for: contactRefetched)
contactViewController.contactStore = AppDelegate.appDelegate.contactStore
contactViewController.displayedPropertyKeys = keys
self.navigationController?.pushViewController(contactViewController, animated: true)
}
}
catch {
print("Unable to refetch the selected contact.", separator: "", terminator: "\n")
}
}
})
}
}
}

extension ViewController: AddContactViewControllerDelegate {


func didFetchContacts(_ contacts: [CNContact]) {
for contact in contacts {
self.contacts.append(contact)
}
tblContacts.reloadData()
}
}
AddContactViewController.swift
import UIKit
import Contacts
import ContactsUI

protocol AddContactViewControllerDelegate {

CSPIT 192
Ce373:Mobile Application Development 16CE068

func didFetchContacts(_ contacts: [CNContact])


}

class AddContactViewController: UIViewController {

@IBOutlet weak var txtLastName: UITextField!


@IBOutlet weak var pickerMonth: UIPickerView!

let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October",
"November", "December"]

var currentlySelectedMonthIndex = 1
var delegate: AddContactViewControllerDelegate!

override func viewDidLoad() {


super.viewDidLoad()

pickerMonth.delegate = self
txtLastName.delegate = self

let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self,


action: #selector(AddContactViewController.performDoneItemTap))
navigationItem.rightBarButtonItem = doneBarButtonItem
}
}
// MARK: IBAction functions

extension AddContactViewController: CNContactPickerDelegate {


@IBAction func showContacts(_ sender: AnyObject) {
let contactPickerViewController = CNContactPickerViewController()

contactPickerViewController.predicateForEnablingContact = NSPredicate(format: "birthday != nil")

contactPickerViewController.delegate = self

contactPickerViewController.displayedPropertyKeys = [CNContactGivenNameKey,
CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey]

present(contactPickerViewController, animated: true, completion: nil)


}

func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {


delegate.didFetchContacts([contact])
navigationController?.popViewController(animated: true)
}
}

// MARK: UIPickerView Delegate and Datasource functions


extension AddContactViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return months.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return months[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
currentlySelectedMonthIndex = row + 1
}
}

// MARK: UITextFieldDelegate functions


extension AddContactViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
AppDelegate.appDelegate.requestForAccess { (accessGranted) -> Void in
if accessGranted {
let predicate = CNContact.predicateForContacts(matchingName: self.txtLastName.text!)

CSPIT 193
Ce373:Mobile Application Development 16CE068

let keys = [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName),


CNContactEmailAddressesKey, CNContactBirthdayKey] as [Any]
var contacts = [CNContact]()
var warningMessage: String!
let contactsStore = AppDelegate.appDelegate.contactStore
do {
contacts = try contactsStore.unifiedContacts(matching: predicate, keysToFetch: keys as!
[CNKeyDescriptor])

if contacts.count == 0 {
warningMessage = "No contacts were found matching the given name."
}
} catch {
warningMessage = "Unable to fetch contacts."
}

if let warningMessage = warningMessage {


DispatchQueue.main.async {
Helper.show(message: warningMessage)
}
} else {
DispatchQueue.main.async {
self.delegate.didFetchContacts(contacts)
self.navigationController?.popViewController(animated: true)
}
}
}
}

return true
}

// MARK: Custom functions

@objc func performDoneItemTap() {


AppDelegate.appDelegate.requestForAccess { (accessGranted) -> Void in
if accessGranted {
var contacts = [CNContact]()

let keys = [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName),


CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey] as [Any]

do {
let contactStore = AppDelegate.appDelegate.contactStore
try contactStore.enumerateContacts(with: CNContactFetchRequest(keysToFetch: keys as!
[CNKeyDescriptor])) { [weak self] (contact, pointer) -> Void in

if contact.birthday != nil && contact.birthday!.month == self?.currentlySelectedMonthIndex {


contacts.append(contact)
}
}

DispatchQueue.main.async {
self.delegate.didFetchContacts(contacts)
self.navigationController?.popViewController(animated: true)
}
}
catch let error as NSError {
print(error.description, separator: "", terminator: "\n")
}
}
}
}
}
CreateContactViewController.swift

CSPIT 194
Ce373:Mobile Application Development 16CE068

import UIKit
import Contacts
class CreateContactViewController: UIViewController {
@IBOutlet weak var txtFirstname: UITextField!
@IBOutlet weak var txtLastname: UITextField!
@IBOutlet weak var txtHomeEmail: UITextField!
@IBOutlet weak var datePicker: UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
txtFirstname.delegate = self
txtLastname.delegate = self
txtHomeEmail.delegate = self
let saveBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.save, target: self,
action: #selector(CreateContactViewController.createContact))
navigationItem.rightBarButtonItem = saveBarButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@objc func createContact() {
let newContact = CNMutableContact()

newContact.givenName = txtFirstname.text!
newContact.familyName = txtLastname.text!

if let homeEmail = txtHomeEmail.text {


let homeEmail = CNLabeledValue(label: CNLabelHome, value: homeEmail as NSString)
newContact.emailAddresses = [homeEmail]
}
let birthdayComponents = Calendar.current.dateComponents([Calendar.Component.year,
Calendar.Component.month, Calendar.Component.day], from: datePicker.date)
newContact.birthday = birthdayComponents
do {
let saveRequest = CNSaveRequest()
saveRequest.add(newContact, toContainerWithIdentifier: nil)
try AppDelegate.appDelegate.contactStore.execute(saveRequest)
navigationController?.popViewController(animated: true)
} catch {
Helper.show(message: "Unable to save the new contact.")
}
}
}
extension CreateContactViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
ContactBirthdayCell.swift
import UIKit
class ContactBirthdayCell: UITableViewCell {
@IBOutlet weak var lblFullname: UILabel!
@IBOutlet weak var lblBirthday: UILabel!
@IBOutlet weak var imgContactImage: UIImageView!
@IBOutlet weak var lblEmail: UILabel!

override func awakeFromNib() {


super.awakeFromNib()
imgContactImage.layer.cornerRadius = 25.0
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}

CSPIT 195
Ce373:Mobile Application Development 16CE068

Output:

CSPIT 196
Ce373:Mobile Application Development 16CE068

PRACTICAL 20

20) Aim: Deploying all Apps in Particular App Store/Hosting


A)On app store

1. Is Your Application Ready?


Step 1: Testing
An application isn't necessarily ready when you've written the last line of code or implemented the final feature of
the application's specification.

Have you tested your application on one or more physical devices? Have you profiled your application for
memory leaks and performance issues? Does your application crash from time to time?

The family of iOS devices has grown substantially over the years, and it is important to test your application on as
many iOS devices as you can lay your hands on. Common issues include not optimizing an application for
certain screen sizes. The iOS Simulator is a great tool, but it runs on your Mac, which has more memory and
processing power than the phone in your pocket.

Apple's Review Process isn't airtight, but it is very capable of identifying problems that might affect your
application's user experience. If your application crashes from time to time or it becomes slow after ten minutes
of use, then you have some work to do before submitting it to the App Store.

Even if Apple's review team doesn't spot the problem, your users will. If the people using your application are not
pleased, they will leave bad reviews on the App Store, which may harm sales or inhibit downloads.

Step 2: Rules and Guidelines


As I mentioned earlier, Apple provides developers with a number of documents that are a great help during the
creation and development process of your application.

The documents that you should be aware of are the iOS Human Interface Guidelines and the App Store Review
Guidelines. Despite the availability of these documents, it seems that few developers take the time to browse
them, let alone read them. It shouldn't be a surprise that some applications are therefore rejected even though
the reason for the rejection is clearly stated in these documents.

Even if you don't intend to read the iOS Human Interface Guidelines or the App Store Review Guidelines, it is
important to know some of the rules that they talk about. Take a look at the short list below to get an idea of what
your application should and shouldn't do.

Your application:

 shouldn't crash

 shouldn't use private APIs

 shouldn't replicate the functionality of native applications

 should use In App Purchase for in-app (financial) transactions

 shouldn't use the camera or microphone without the user's knowledge

CSPIT 197
Ce373:Mobile Application Development 16CE068

 should only use artwork that is your copyright or that you have permission to use

Keep in mind that this is a tiny subset of the guidelines included in the aforementioned documents. The majority
of the rules and guidelines are trivial, but some are not, and you might even violate some of them inadvertently.

Let me give you an example. Before Apple started using its own maps (a really long time ago), the MapKit
framework used Google's maps. This was clear to the user because of the small Google logo in the bottom left
corner of each map. However, if some part of your application's user interface covered or obscured Google's
logo, your application would get rejected. This rule seems trivial, but it is a rule that is easily violated if you're not
careful. Even automated tests won't cover you in this case.

2. Prerequisites

Before you can even start thinking about submitting your application to the App Store, you need to make sure
that you have an App ID, a valid distribution certificate, and a valid provisioning profile. Let me show you what
this entails.

Step 1: App ID
Every application needs an App ID or application identifier. There are two types of application identifiers:
an explicit App ID and a wildcard App ID. A wildcard App ID can be used for building and installing multiple
applications. Despite the convenience of a wildcard App ID, an explicit App ID is required if your application uses
iCloud or makes use of other iOS features, such as Game Center, Apple Push Notifications, or In App Purchase.

Step 2: Distribution Certificate


To submit an application to the App Store, you need to create an iOS provisioning profile for distribution. To
create such a provisioning profile, you first need to create a distribution certificate. The process for creating a
distribution certificate is very similar to creating a development certificate. If you have tested your application on a
physical device, then you are probably already familiar with the creation of a development certificate.

If you need to refresh your memory, I suggest reading Apple's guide, Code Signing your Apps, about signing
certificates and provisioning profiles. The process is not difficult once you understand how the various pieces of
the puzzle fit together.

Step 3: Provisioning Profile


Once you've created an App ID and a distribution certificate, you can create an iOS provisioning profile for
distributing your application through the App Store.

Keep in mind that you cannot use the same provisioning profile that you use for ad hoc distribution. You need to
create a separate provisioning profile for App Store distribution. If you use a wildcard App ID for your project, then
you can use the same provisioning profile for multiple applications.

Step 4: Build Settings


With the App ID, distribution certificate, and provisioning profile in place, it is time to configure your target's build
settings in Xcode. This means selecting the target from the list of targets in Xcode's Project Navigator, opening
the Build Settings tab at the top, and updating the settings in the Signing section. You will need to set the Code
Signing to Automatic.

CSPIT 198
Ce373:Mobile Application Development 16CE068

Even though the code signing process is fairly simple once you understand it, it is something that trips up a lot of
developers. I don't know a single Cocoa developer who hasn't run into code signing issues at some point in their
career. Once you've cleared this hurdle, the rest of the submission process is fairly easy.

Step 5: Deployment Target


It is useful to think a little about your application's deployment target. Each target in an Xcode project has a
deployment target, which indicates the minimum version of the operating system that the application can run on.

It is up to you to set the deployment target, but keep in mind that modifying the deployment target is not
something you can do without consequences once your application is in the App Store. If you increase the
deployment target for an update of your application, then users who have already purchased your application but
don't meet the new deployment target cannot run the update.

It gets really problematic when a user downloads an update through iTunes (not the device), replacing the
previous version on their computer, and then discovers that the new update doesn't run on their device.

3. Assets

Step 1: Icons
You probably know that an application icon is a vital component of every iOS application, but you need to make
sure that your application ships with the correct sizes of the artwork. Take a look at the table below:

It goes without saying that you don't need to include an application icon for the iPad/iPad Mini device family if
your application only targets the iPhone/iPod Touch device family, and vice versa.

Step 2: Screenshots
Each application can have up to five screenshots and three previews, and you must provide at least one. If you
are developing a universal application, then you need to provide separate screenshots for each device.

It is important to spend some time thinking about the screenshots. Your application's screenshots are often the
only thing that a customer can use to decide whether to purchase or download your application or not.

CSPIT 199
Ce373:Mobile Application Development 16CE068

What a lot of developers don't know is that the screenshots don't have to be actual screenshots. The hard rule is
that the size of each screenshot needs to be that of the screen size of the target device. Many companies are
creative with this rule. Take a look at the screenshots of Where's My Water?, for example, which include labels
highlighting key features of the app. By using this strategy, you can make screenshots much more attractive and
compelling.

Step 3: Metadata
Before you submit your application, it is a good idea to have your application's metadata at hand. This includes:

 your application's name

 the version number

 the primary (and an optional secondary) category

 a concise description

 keywords

 a support URL

If you are submitting an update, then you can also provide information for the What's New in this
Version section.

Does your application require users to sign in? Then you also need to provide Apple with a test or demo account
to make sure that the review team can immediately sign in and use your application without first having to sign up
for an account.

4. Submission Preparation

The submission process has become much easier these days. You can now validate and submit an application
using Xcode, for example. First, however, you need to create your application in iTunes Connect.

Visit iTunes Connect, sign in with your iOS developer account, and click Manage Your Apps on the right. Click
the Add New App in the top left, select iOS App, and fill out the form.

Step 1: Basic Information


The App Name, which needs to be unique, is the name of your application as it will appear in the App Store. This
can be different than the name that is displayed below your application icon on the home screen, but it is
recommended to choose the same name.

The SKU Number is a unique string that identifies your application. I usually use the application's bundle
identifier.

The last piece of information is the Bundle ID of your application. This means selecting the (wildcard or explicit)
App ID that you created earlier from the drop-down menu.

CSPIT 200
Ce373:Mobile Application Development 16CE068

Step 2: Price and Availability


In the next step, you specify your application's price and availability. Apple works with price tiers so that you don't
have to specify a price for each country that Apple operates in. You can also specify in which stores your
application should—or shouldn't—be available.

The information that you enter in this step can be modified once your application is live in the App Store. In other
words, you can change the price and availability of an application without having to submit an update. You can
easily do this by selecting the Pricing and Availability tab on the left of your app's iTunes Connect page.

CSPIT 201
Ce373:Mobile Application Development 16CE068

Step 3: Metadata
We've already covered the application's metadata. The only aspect that I haven't talked about yet is your
application's rating. Based on your application's content and functionality, it is given a rating. This rating is not
only useful for telling users about your application's content and features, but is also used by the operating
system for the parental controls features.

It is strongly recommended that you don't try to outsmart the rating system. Apple is well aware of this strategy
and will reject your application if it doesn't agree with the rating that you have set. There are many other things
here that you may need to adjust based on your app, but we won't go over them since they are pretty self-
explanatory. To do this, go to the App Information tab in the left pane.

5. Uploading the App Binary


To submit your app, you need to create an archive. You can only create an archive by building your application
on a generic device. If you select the iOS Simulator in the active scheme, you will notice that the Archive option
in Xcode's Product menu is grayed out. Connect an iOS device to your Mac, select it in the active scheme, and
select Archive from Xcode's Product menu.

CSPIT 202
Ce373:Mobile Application Development 16CE068

If all went well, you should now have an archive, and Xcode's Organizer should automatically open and show you
the archive you just created. Select the archive from the list and click the Upload to App Store... button on the
right. The application binary is then uploaded to Apple's servers.

Advertisement

6. Waiting

If the submission process went without problems, your application's status will change to Waiting for Review. It
takes several days for Apple to review your app, and the time it takes tends to fluctuate over time.

CSPIT 203
Ce373:Mobile Application Development 16CE068

b)on Play store


1. Create Your Android App

 First thing for uploading your App on Play Store is to Develop your App and make it Store-Ready.

 Use Proper theme, colors, graphics, strings and icons.

2. Make its Signed Apk(Using Release Keystore)

This is a graphical way to generate a release keystore

CSPIT 204
Ce373:Mobile Application Development 16CE068

To generate keystores for signing Android apps at the command line, use:
$ keytool -genkey -v -keystore my-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

A debug keystore which is used to sign an Android app during development needs a specific alias and password
combination as dictated by Google. To create a debug keystore, use:
$ keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android
-keyalg RSA -keysize 2048 -validity 10000

Keystore name: “debug.keystore”


Keystore password: “android”
Key alias: “androiddebugkey”
Key password: “android”
3. Login to your Gmail Account and visit this link : https://play.google.com/apps/publish/

And Go to this Link — https://play.google.com/apps/publish/

4. Create a Merchant Account

CSPIT 205
Ce373:Mobile Application Development 16CE068

5. Mandatory Fields needed in App Listing:

— Title
 — Short Desc
 — Full Desc
 — App Screenshots(JPEG or 24-bit PNG (no alpha))(Min-2,Max-8)(Min-320px,Max-3840px)
 — Hi-res icon(512 x 512)(32-bit PNG (with alpha))
 — Feature Graphic(1024 w x 500 h)(JPG or 24-bit PNG (no alpha))
 — App Type
 — Category
 — Content Rating
 — Developer/Company Email
 — Privacy Policy Url
 — And some other details

CSPIT 206

You might also like