ThingMaker in Swift
Allan Shih
Agenda
● XCode IDE
● ThingMaker: Category List UI
● Model-View-Controller (MVC) Structure
● Make a Server Request
● Demo
● Todo List
● Reference
XCode IDE
Editor
Assistant Editor View
Interface Builder View
Autolayout Constraints
Autolayout Misplaced View
Connect outlets to ViewController
Connect actions to ViewController
ThingMaker Category List UI
Category List Storyboard
Model-View-Controller (MVC) Structure
Project structure
● CodeData
● Helpers
● Service
● Extensions
● Views
● Models
● Controllers
Model
public struct CategoryList {
public var filterGroups : Array<FilterGroups>?
public var filterKey : String?
public var appStoreAPIVersion : Double?
public var status : Int?
public init() {}
/**
Constructs the object based on the given dictionary.
- parameter dictionary: NSDictionary from JSON.
- returns: CategoryList Instance.
*/
public init?(dictionary: NSDictionary) {
if (dictionary["filterGroups"] != nil) {
filterGroups = FilterGroups.modelsFromDictionaryArray(array: dictionary["filterGroups"] as! NSArray)
}
filterKey = dictionary["filterKey"] as? String
appStoreAPIVersion = dictionary["appStoreAPIVersion"] as? Double
status = dictionary["status"] as? Int
}
}
View
import UIKit
class CategoryCollectionViewCell: UICollectionViewCell
{
@IBOutlet weak var categoryImage: UIImageView!
@IBOutlet weak var categoryLabel: UILabel!
}
ViewController
class CategoryListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var categoryListCollectionView: UICollectionView!
private var filterGroups = [FilterGroups]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.categoryListCollectionView.delegate = self
self.categoryListCollectionView.dataSource = self
CategoryList.fetchCategoryList(completion: { [weak self] filterGroups in
guard let groups = filterGroups, !groups.isEmpty else {
print("group is empy")
return
}
self?.updateData(filterGroups: groups)
})
}
}
Making a Server Request
public static func fetchCategoryList(completion: @escaping ([FilterGroups]?) -> Void) {
let headers: HTTPHeaders = [
"APIKey": "3a484891-98f5-40c7-89ce-2a849aad8d14",
"Accept": "application/json"
]
Alamofire.request("http://stg-stable2.fuhu.org/appstore/v2/filter/TK", headers: headers)
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON { response in
guard response.result.isSuccess else {
print("Error while fetching category list: (response.result.error)")
return
}
guard let json = response.result.value as? NSDictionary else {
print("Response couldn't convert to NSDictionary.")
return
}
print("Success with json: (json)")
guard let filterGroups = CategoryList(dictionary: json)?.filterGroups, !filterGroups.isEmpty else {
print("No FilterGroups.")
return
}
completion(filterGroups)
}
Return result to Completion handler
class CategoryListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var categoryListCollectionView: UICollectionView!
private var filterGroups = [FilterGroups]()
override func viewDidLoad() {
...
CategoryList.fetchCategoryList(completion: { [weak self] filterGroups in
guard let groups = filterGroups, !groups.isEmpty else {
print("group is empy")
return
}
self?.updateData(filterGroups: groups)
})
}
func updateData(filterGroups: [FilterGroups]) {
self.filterGroups = filterGroups
self.categoryListCollectionView.reloadData()
}
...
}
Update UI - Label
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) ->
UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for:
indexPath) as! CategoryCollectionViewCell
guard let filterGroup = filterGroups[safe: indexPath.row]
else {
print("no filter group")
return cell
}
// set labels
guard let categoryLabel = filterGroup.filterGroupName else {
print("no category label")
return cell
}
cell.categoryLabel.text = categoryLabel
...
}
Update UI - Image
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) ->
UICollectionViewCell {
...
// set images
guard let imageUrl = filterGroup.filterGroupImage?.imageUrl,
let url = URL(string: imageUrl) else {
print("Can't convert image url to URL.")
return cell
}
print("imageUrl: (imageUrl)")
cell.categoryImage.kf.setImage(with: url,
placeholder: nil,
options: [.transition(.fade(1))],
progressBlock: nil,
completionHandler: nil)
return cell
}
Demo
Todo List
● HttpDataController
● Core Data (data cache)
● Cocoa Touch Framework
● MVVM: Model-View-ViewModel
Reference
● The Swift Programming Language (Swift 3.0.1)
● Developing iOS 9 Apps with Swift
● Alamofire is an HTTP networking library written in Swift.
● KingFisher is a lightweight, pure-Swift library for downloading and caching
images from the web.

ThingMaker in Swift

  • 1.
  • 2.
    Agenda ● XCode IDE ●ThingMaker: Category List UI ● Model-View-Controller (MVC) Structure ● Make a Server Request ● Demo ● Todo List ● Reference
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
    Connect outlets toViewController
  • 10.
    Connect actions toViewController
  • 11.
  • 12.
  • 13.
  • 14.
    Project structure ● CodeData ●Helpers ● Service ● Extensions ● Views ● Models ● Controllers
  • 15.
    Model public struct CategoryList{ public var filterGroups : Array<FilterGroups>? public var filterKey : String? public var appStoreAPIVersion : Double? public var status : Int? public init() {} /** Constructs the object based on the given dictionary. - parameter dictionary: NSDictionary from JSON. - returns: CategoryList Instance. */ public init?(dictionary: NSDictionary) { if (dictionary["filterGroups"] != nil) { filterGroups = FilterGroups.modelsFromDictionaryArray(array: dictionary["filterGroups"] as! NSArray) } filterKey = dictionary["filterKey"] as? String appStoreAPIVersion = dictionary["appStoreAPIVersion"] as? Double status = dictionary["status"] as? Int } }
  • 16.
    View import UIKit class CategoryCollectionViewCell:UICollectionViewCell { @IBOutlet weak var categoryImage: UIImageView! @IBOutlet weak var categoryLabel: UILabel! }
  • 17.
    ViewController class CategoryListViewController: UIViewController,UICollectionViewDelegate, UICollectionViewDataSource { @IBOutlet weak var categoryListCollectionView: UICollectionView! private var filterGroups = [FilterGroups]() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.categoryListCollectionView.delegate = self self.categoryListCollectionView.dataSource = self CategoryList.fetchCategoryList(completion: { [weak self] filterGroups in guard let groups = filterGroups, !groups.isEmpty else { print("group is empy") return } self?.updateData(filterGroups: groups) }) } }
  • 18.
    Making a ServerRequest public static func fetchCategoryList(completion: @escaping ([FilterGroups]?) -> Void) { let headers: HTTPHeaders = [ "APIKey": "3a484891-98f5-40c7-89ce-2a849aad8d14", "Accept": "application/json" ] Alamofire.request("http://stg-stable2.fuhu.org/appstore/v2/filter/TK", headers: headers) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .responseJSON { response in guard response.result.isSuccess else { print("Error while fetching category list: (response.result.error)") return } guard let json = response.result.value as? NSDictionary else { print("Response couldn't convert to NSDictionary.") return } print("Success with json: (json)") guard let filterGroups = CategoryList(dictionary: json)?.filterGroups, !filterGroups.isEmpty else { print("No FilterGroups.") return } completion(filterGroups) }
  • 19.
    Return result toCompletion handler class CategoryListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { @IBOutlet weak var categoryListCollectionView: UICollectionView! private var filterGroups = [FilterGroups]() override func viewDidLoad() { ... CategoryList.fetchCategoryList(completion: { [weak self] filterGroups in guard let groups = filterGroups, !groups.isEmpty else { print("group is empy") return } self?.updateData(filterGroups: groups) }) } func updateData(filterGroups: [FilterGroups]) { self.filterGroups = filterGroups self.categoryListCollectionView.reloadData() } ... }
  • 20.
    Update UI -Label func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryCell", for: indexPath) as! CategoryCollectionViewCell guard let filterGroup = filterGroups[safe: indexPath.row] else { print("no filter group") return cell } // set labels guard let categoryLabel = filterGroup.filterGroupName else { print("no category label") return cell } cell.categoryLabel.text = categoryLabel ... }
  • 21.
    Update UI -Image func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { ... // set images guard let imageUrl = filterGroup.filterGroupImage?.imageUrl, let url = URL(string: imageUrl) else { print("Can't convert image url to URL.") return cell } print("imageUrl: (imageUrl)") cell.categoryImage.kf.setImage(with: url, placeholder: nil, options: [.transition(.fade(1))], progressBlock: nil, completionHandler: nil) return cell }
  • 22.
  • 23.
    Todo List ● HttpDataController ●Core Data (data cache) ● Cocoa Touch Framework ● MVVM: Model-View-ViewModel
  • 24.
    Reference ● The SwiftProgramming Language (Swift 3.0.1) ● Developing iOS 9 Apps with Swift ● Alamofire is an HTTP networking library written in Swift. ● KingFisher is a lightweight, pure-Swift library for downloading and caching images from the web.