Iphone App Develpment
Iphone App Develpment
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.
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
productNameLabel.text = product?.name
ContactViewController.shift
import UIKit
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)
}
CSPIT 98
Ce373:Mobile Application Development 16CE068
ProductsTableViewController.swift
import UIKit
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
}
CSPIT 99
Ce373:Mobile Application Development 16CE068
CSPIT 100
Ce373:Mobile Application Development 16CE068
Lovetweet
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CAAnimationDelegate {
// 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
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
// 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!
initCircleButton(playPauseButton)
initCircleButton(lapRestButton)
lapRestButton.isEnabled = false
lapsTableView.delegate = self;
lapsTableView.dataSource = self;
}
// MARK: - UI Settings
override var shouldAutorotate : Bool {
return false
}
// MARK: - Actions
@IBAction func playPauseTimer(_ sender: AnyObject) {
CSPIT 103
Ce373:Mobile Application Development 16CE068
lapRestButton.isEnabled = true
if !isPlay {
unowned let weakSelf = self
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)
}
}
CSPIT 104
Ce373:Mobile Application Development 16CE068
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return laps.count
}
return cell
}
}
// MARK: - Extension
fileprivate extension Selector {
CSPIT 105
Ce373:Mobile Application Development 16CE068
Stopwatch.swift
import Foundation
override init() {
counter = 0.0
timer = Timer()
}
}
Output:
CSPIT 106
Ce373:Mobile Application Development 16CE068
To do
Utils.swift
import Foundation
ToDoItem.swift
import Foundation
CSPIT 107
Ce373:Mobile Application Development 16CE068
self.date = date
}
}
ViewController.swift
import UIKit
navigationItem.leftBarButtonItem = editButtonItem
CSPIT 108
Ce373:Mobile Application Development 16CE068
}
}
}
}
if todos.count != 0 {
return todos.count
} else {
let messageLabel: UILabel = UILabel()
self.todoTableView.backgroundView = messageLabel
self.todoTableView.separatorStyle = UITableViewCellSeparatorStyle.none
return 0
}
}
return cell
}
}
CSPIT 109
Ce373:Mobile Application Development 16CE068
}
}
DetailViewController.swift
import UIKit
class DetailViewController: UIViewController {
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
// MARK: - Properties
var detailViewController: DetailViewController? = nil
var candies = [Candy]()
var filteredCandies = [Candy]()
let searchController = UISearchController(searchResultsController: nil)
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()
CSPIT 112
Ce373:Mobile Application Development 16CE068
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive {
return filteredCandies.count
}
return candies.count
}
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
}
}
}
DetailViewController.shift
import UIKit
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
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
filteredPokemons = pokemons
}
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
}
delegate?.pokemonSelected(pokemon)
// MARK: - UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredPokemons.count
}
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
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
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
filteredPokemons = pokemons
}
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
}
delegate?.pokemonSelected(pokemon)
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
}
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
}
}
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
}
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
indicator = UIActivityIndicatorView()
indicator.center = CGPoint(x: pokeImageView.bounds.midX, y: pokeImageView.bounds.midY)
indicator.activityIndicatorViewStyle = .whiteLarge
indicator.startAnimating()
pokeImageView.addSubview(indicator)
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"),
CSPIT 122
Ce373:Mobile Application Development 16CE068
CSPIT 123
Ce373:Mobile Application Development 16CE068
Output:
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
}
NewsTableViewController.swift
import UIKit
tableView.estimatedRowHeight = 140
tableView.rowHeight = UITableViewAutomaticDimension
CSPIT 125
Ce373:Mobile Application Development 16CE068
tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
DispatchQueue.main.async {
self?.tableView.reloadSections(IndexSet(integer: 0), with: .none)
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let rssItems = rssItems else {
return 0
}
return rssItems.count
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
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
CSPIT 127
Ce373:Mobile Application Development 16CE068
FaceBookMe
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
}
Specs.swift
import UIKit
CSPIT 128
Ce373:Mobile Application Development 16CE068
UIColor+Extension.swift
import UIKit
CSPIT 129
Ce373:Mobile Application Development 16CE068
import Foundation
CSPIT 130
Ce373:Mobile Application Development 16CE068
]
]
]
}
}
FBMeBaseViewController.swift
import UIKit
FBMeViewController.swift
import UIKit
title = "Facebook"
navigationController?.navigationBar.barTintColor = Specs.color.tint
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
CSPIT 131
Ce373:Mobile Application Development 16CE068
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[tableView]-0-|",
options: .directionLeadingToTrailing, metrics: nil, views: ["tableView": tableView]))
}
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 title == user.name {
cell.detailTextLabel?.text = modelForRow[TableKeys.SubTitle]
}
return cell
}
}
CSPIT 132
Ce373:Mobile Application Development 16CE068
if title == user.name {
return 64.0
} else {
return 44.0
}
}
FBMeBaseCell.swift
import UIKit
backgroundColor = Specs.color.white
textLabel?.textColor = Specs.color.black
textLabel?.font = Specs.font.large
detailTextLabel?.font = Specs.font.small
detailTextLabel?.textColor = Specs.color.gray
}
CSPIT 133
Ce373:Mobile Application Development 16CE068
FBMeUser.swift
import UIKit
class FBMeUser {
var name: String
var avatarName: String
var education: String
Output:
Interests
AppDelegate.swift
import UIKit
@UIApplicationMain
CSPIT 134
Ce373:Mobile Application Development 16CE068
}
InterestCollectionViewCell.swift
import UIKit
CSPIT 135
Ce373:Mobile Application Development 16CE068
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!
// MARK: - Private
CSPIT 136
Ce373:Mobile Application Development 16CE068
}
}
HomeViewController.swift
import UIKit
// MARK: - UICollectionViewDataSource
extension HomeViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return interests.count
}
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
CSPIT 137
Ce373:Mobile Application Development 16CE068
}
}
Output:
PhotoScroll
AppDelegate.shift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
}
UIImage
import UIKit
extension UIImage {
CSPIT 138
Ce373:Mobile Application Development 16CE068
PhotoCell
import UIKit
CollectionViewController
import UIKit
// MARK: UICollectionViewDataSource
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
CSPIT 139
Ce373:Mobile Application Development 16CE068
// MARK:UICollectionViewDelegateFlowLayout
extension CollectionViewController : UICollectionViewDelegateFlowLayout {
PhotoCommentViewController.swift
import UIKit
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)
}
ZoomedPhotoViewController.swift
CSPIT 141
Ce373:Mobile Application Development 16CE068
import UIKit
updateMinZoomScale(forSize: view.bounds.size)
}
view.layoutIfNeeded()
}
scrollView.minimumZoomScale = minScale
/// 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
}
CSPIT 142
Ce373:Mobile Application Development 16CE068
updateConstraints(forSize: view.bounds.size)
}
}
ManagePageViewController.swift
import UIKit
dataSource = self
CSPIT 143
Ce373:Mobile Application Development 16CE068
return nil
}
return viewPhotoCommentController(index: index + 1)
}
return nil
}
CSPIT 144
Ce373:Mobile Application Development 16CE068
Animation
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
ViewController.swift
import UIKit
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"]
func animateTable() {
masterTableView.reloadData()
// MARK: - Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueDetailIdentifier {
let detailView = segue.destination as! DetailViewController
let indexPath = masterTableView.indexPathForSelectedRow
// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat(headerHeight)
}
CSPIT 146
Ce373:Mobile Application Development 16CE068
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
return cell
}
}
DetailViewController.swift
import UIKit
// 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()
}
CSPIT 147
Ce373:Mobile Application Development 16CE068
// MARK: - IBAction
@IBAction func didTapAnimate(_ sender: AnyObject) {
switch barTitle {
case "2-Color":
changeColor(UIColor.green)
case "Multicolor":
multiColor(UIColor.green, UIColor.blue)
case "Pop":
Pop()
default:
let alert = makeAlert("Alert", message: "The animation not implemented yet", actionTitle: "OK")
self.present(alert, animated: true, completion: nil)
}
}
CSPIT 148
Ce373:Mobile Application Development 16CE068
CSPIT 149
Ce373:Mobile Application Development 16CE068
Common.swift
import Foundation
import UIKit
func drawRectView(_ color: UIColor, frame: CGRect, center: CGPoint) -> UIView {
let view = UIView(frame: frame)
view.center = center
view.backgroundColor = color
return view
}
CSPIT 150
Ce373:Mobile Application Development 16CE068
shapeLayer.fillColor = UIColor.red.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 3.0
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
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
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?
}
// 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")
}
CSPIT 153
Ce373:Mobile Application Development 16CE068
// 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")
}
TagsColorsViewController
import UIKit
// MARK: - Properties
var tags: [String]?
var colors: [PhotoColor]?
var tableViewController: TagsColorsTableViewController!
// MARK: - IBOutlets
@IBOutlet var segmentedControl: UISegmentedControl!
// 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 {
ViewController
import UIKit
import Alamofire
// 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]?
if !UIImagePickerController.isSourceTypeAvailable(.camera) {
takePictureButton.setTitle("Select Photo", for: UIControlState())
}
}
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
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
}
// MARK: - UIImagePickerControllerDelegate
extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
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
dismiss(animated: true)
}
}
extension ViewController {
func upload(image: UIImage,
progressCompletion: @escaping (_ percent: Float) -> Void,
CSPIT 156
Ce373:Mobile Application Development 16CE068
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
}
CSPIT 157
Ce373:Mobile Application Development 16CE068
completion(tags)
}
}
completion(photoColors)
}
}
}
ImaggaRouter.swift
import Foundation
import Alamofire
case content
case tags(String)
case colors(String)
CSPIT 158
Ce373:Mobile Application Development 16CE068
return .post
case .tags, .colors:
return .get
}
}
CSPIT 159
Ce373:Mobile Application Development 16CE068
MarsLink
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
DateSortable.swift
import Foundation
protocol DateSortable {
var date: Date { get }
}
JournalEntry.swift
import Foundation
NSObject+IGListDiffable.swift
CSPIT 160
Ce373:Mobile Application Development 16CE068
import Foundation
import IGListKit
// MARK: - IGListDiffable
extension NSObject: IGListDiffable {
SolFormatter.swift
import Foundation
struct SolFormatter {
Weather.swift
import UIKit
enum WeatherCondition: String {
case cloudy = "Cloudy"
case sunny = "Sunny"
case partlyCloudy = "Partly Cloudy"
case dustStorm = "Dust Storm"
CSPIT 161
Ce373:Mobile Application Development 16CE068
}
}
}
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
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
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
return cell
}
MessageSectionController.swift
import UIKit
import IGListKit
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
cell.titleLabel.text = message.user.name.uppercased()
cell.messageLabel.text = message.text
return cell
}
CSPIT 163
Ce373:Mobile Application Development 16CE068
WeatherSectionController.swift
import UIKit
import IGListKit
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
if index == 0 {
return CGSize(width: width, height: 70)
} else {
return CGSize(width: width, height: 40)
}
}
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
}
}
CSPIT 164
Ce373:Mobile Application Development 16CE068
JournalLoader.swift
import Foundation
class JournalEntryLoader {
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
private func delay(time: Double = 1, execute work: @escaping @convention(block) () -> Swift.Void) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
work()
}
}
CSPIT 165
Ce373:Mobile Application Development 16CE068
class Pathfinder {
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 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
}
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
}
}
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)
}
}
WxScanner.swift
import Foundation
class WxScanner {
ClassicFeedViewController.swift
import UIKit
CSPIT 167
Ce373:Mobile Application Development 16CE068
loader.loadLatest()
}
//MARK: UICollectionViewDataSource
extension ClassicFeedViewController: UICollectionViewDataSource {
//MARK: UICollectionViewDelegateFlowLayout
extension ClassicFeedViewController: UICollectionViewDelegateFlowLayout {
FeedViewController.swift
CSPIT 168
Ce373:Mobile Application Development 16CE068
import UIKit
import IGListKit
func setupUI() {
view.addSubview(collectionView)
}
func setupDateSource() {
loader.loadLatest()
adapter.collectionView = collectionView
adapter.dataSource = self
pathfinder.delegate = self
pathfinder.connect()
}
setupUI()
setupDateSource()
}
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()
}
CSPIT 170
Ce373:Mobile Application Development 16CE068
JournalEntryCell.swift
import UIKit
CSPIT 171
Ce373:Mobile Application Development 16CE068
}
JournalEntryDateCell.swift
import UIKit
MessageCell.swift
CSPIT 172
Ce373:Mobile Application Development 16CE068
import UIKit
CSPIT 173
Ce373:Mobile Application Development 16CE068
WeatherSummaryCell.swift
import UIKit
CSPIT 174
Ce373:Mobile Application Development 16CE068
return label
}()
}
Output:
CSPIT 175
Ce373:Mobile Application Development 16CE068
Scene detector
AppDelegate.shift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
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 {
scene.image = image
CSPIT 177
Ce373:Mobile Application Development 16CE068
} catch {
print(error)
}
}
}
}
Output:
NotificationUI
AppDelegate.swift
import UIKit
import UserNotifications
return true
}
CSPIT 178
Ce373:Mobile Application Development 16CE068
do {
let attachment = try UNNotificationAttachment(identifier: "Swift", url: url, options: nil)
content.attachments = [attachment]
} catch {
print("The attachment was not loaded.")
}
}
ViewController.swift
import UIKit
CSPIT 179
Ce373:Mobile Application Development 16CE068
Output:
Scale
AppDelegate.swift
import UIKit
enum Shortcut: String {
case openBlue = "OpenBlue"
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
print("didFinishLaunchingWithOptions called")
var isLaunchedFromQuickAction = false
return !isLaunchedFromQuickAction
CSPIT 180
Ce373:Mobile Application Development 16CE068
return true
}
CSPIT 181
Ce373:Mobile Application Development 16CE068
WeatherExtension
AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
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.
}
}
ViewController.swift
import UIKit
import WeatherKit
weatherLabel.text = ""
temperatureLabel.text = ""
displayCurrentWeather()
}
func displayCurrentWeather() {
// Update location
cityLabel.text = city
countryLabel.text = country
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 {
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.
}
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
}()
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
// 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"
}
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
}
return cell
}
//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)
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 {
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)
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
configureTableView()
}
CSPIT 190
Ce373:Mobile Application Development 16CE068
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"
}
return cell
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
}
}
protocol AddContactViewControllerDelegate {
CSPIT 192
Ce373:Mobile Application Development 16CE068
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October",
"November", "December"]
var currentlySelectedMonthIndex = 1
var delegate: AddContactViewControllerDelegate!
pickerMonth.delegate = self
txtLastName.delegate = self
contactPickerViewController.delegate = self
contactPickerViewController.displayedPropertyKeys = [CNContactGivenNameKey,
CNContactFamilyNameKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey]
CSPIT 193
Ce373:Mobile Application Development 16CE068
if contacts.count == 0 {
warningMessage = "No contacts were found matching the given name."
}
} catch {
warningMessage = "Unable to fetch contacts."
}
return true
}
do {
let contactStore = AppDelegate.appDelegate.contactStore
try contactStore.enumerateContacts(with: CNContactFetchRequest(keysToFetch: keys as!
[CNKeyDescriptor])) { [weak self] (contact, pointer) -> Void in
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!
CSPIT 195
Ce373:Mobile Application Development 16CE068
Output:
CSPIT 196
Ce373:Mobile Application Development 16CE068
PRACTICAL 20
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.
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
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.
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.
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.
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.
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:
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.
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
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.
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
First thing for uploading your App on Play Store is to Develop your App and make it Store-Ready.
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
CSPIT 205
Ce373:Mobile Application Development 16CE068
— 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