第1页
Generic Programming with Protocol in Swift iOS
第2页
Generic Programming with Protocol in Swift iOS
第4页
func swapInt(inout a: Int, inout _ b: Int) { let tmp = a a=b b = tmp
} var someInt = 1 var anotherInt = 5 swapInt(&someInt, &anotherInt)
func swapString(inout a: String, inout _ b: String) { let tmp = a a=b b = tmp
}
第5页
func swapValue<T>(inout a: T, inout _ b: T) { let tmp = a a=b b = tmp
}
var someInt = 1 var anotherInt = 5 var someString = "some" var anotherString = "another" swapValue(&someInt, &anotherInt) swapValue(&someString, &anotherString)
第8页
// ? Optional var optInt: Int? var optInt2: Optional<Int>
public enum Optional<Wrapped> : … { case None case Some(Wrapped) …
}
第9页
enum FailableData<T> { case Error(Error) case Data(T)
}
completion FailableData<Exercise>
dataController.requestOrCreateExerciseWithTopicTask
(task, completion: { data in
switch data {
case .Data(let exercise):
// exercise
Exercise
case .Error(let error):
// Error
}
})
第11页
JSONSerializable
public protocol JSONSerializable { func toJSONObject() -> JSONObject
} class Man: JSONSerializable {
var name: String? var son: Man? func toJSONObject() -> JSONObject {
var json = JSONDictionary() name --> json["name"] son --> json["son"] return json } } man.toJSONObject()
第12页
struct Serialization { static func convertAndAssign<T: JSONSerializable>(property: T?,
inout toJSONObject jsonObject: JSONObject?) -> JSONObject? { if let property = property { jsonObject = property.toJSONObject() } return jsonObject
} } infix operator --> { associativity right precedence 150 } public func --> <T: JSONSerializable>(property: T?, inout jsonObject: JSONObject?) -> JSONObject? {
return Serialization.convertAndAssign(property, toJSONObject: &jsonObject) }
extension String: JSONSerializable { public func toJSONObject() -> JSONObject { return self }
}
CaesarParser
https://github.com/lancy/CaesarParser
第14页
GenericViewWrapper
• ViewWrapper contentView subview
• contentView UIView
• ViewWrapper contentView
• contentView
Wrapper Content
第15页
class GenericViewWrapper<ContentView: UIView>: UIView { /// The configurator type typealias Configurator = (config: GenericViewWrapper<ContentView>)
-> () /// Vertical alignment var verticalAlignment: AlignmentMode = .Fill /// Horizontal alignment var horizontalAlignment: AlignmentMode = .Fill /// The content view var contentView: ContentView /// Init init(contentView: ContentView, configurator: Configurator? = nil) { self.contentView = contentView super.init(frame: CGRectZero) configurator?(config: self) setupUserInterface() } // implementation …
}
第16页
labelWrapper padding
15pt
lazy var labelWrapper: GenericViewWrapper<UILabel> = { let label = UILabel() label.text = "Label" label.font = UIFont.systemFontOfSize(12) let wrapper = GenericViewWrapper(contentView: label) { config in config.horizontalAlignment = .LeadingPadding(15) config.verticalAlignment = .Center } return wrapper
}()
labelWrapper UILabel
labelWrapper.contentView.text = "Text"
第18页
ViewWrapper
class GenericTouchableViewWrapper<ContentView: UIView>: GenericViewWrapper<ContentView> {
private let target: AnyObject private let action: Selector /// Create and return a touchable view wrapper that wrap `contentView` and send `action` to `target` while user touch up inside the wrapper view. Can be config with `configurator`. init(contentView: ContentView, target: AnyObject, action: Selector, configurator: Configurator? = nil) {
self.target = target self.action = action super.init(contentView: contentView, configurator: configurator) } // Handle touch event and send action to target… }
第19页
// ScrollViewWrapper
ContentView
class GenericScrollableViewWrapper<ContentView: UIView>: UIScrollView
//
item
StackLayoutView
class GenericStackLayoutView<ArrangedView: UIView>: UIView
第21页
?
第23页
•
• SectionView • SectionView
HeaderView ItemView
第24页
• ItemView View
• SectionHeaderView View
• ItemView SectionHeaderView ViewModel
第25页
ViewModel
func makeViewModel(paper: Paper) -> ViewModel { let sectionViewModels = paper.chapters.map { chapter in let headerViewModel = HeaderViewModel(chapter) let itemViewModels = chapter.questions.map { question in return ItemViewModel(question) } return SectionViewModel(headerViewModel, itemViewModels) } return ViewModel(sectionViewModels)
} let cardView = CardView(viewModel: makeViewModel())
第26页
ViewModel
/// Able to be created with default initializer protocol Creatable {
/// Default initializer /// /// - returns: Created instance init() } /// Support view model protocol ViewModeled: class { /// View model type associatedtype ViewModel func bindDataWithViewModel(viewModel: ViewModel) }
/// Both Creatable and ViewModeled typealias CreatableViewModeled = protocol<Creatable, ViewModeled>
第27页
StaticSize StaticHeight
/// Size is constant protocol StaticSize: StaticHeight, StaticWidth {
/// Static height static var staticSize: CGSize { get } }
第29页
Protocol
ViewModelClass
ViewClass
GenericCard ViewModelType
GenericCard SectionViewModelType
GenericCard ViewModel
GenericCard SectionViewModel
SectionHeader ViewModel
GenericCard View
GenericCard SectionView
SectionHeader View
GenericCard SectionContentView
ItemViewModel
ItemView
Generic Generic
第30页
GenericCard ViewModelType
protocol GenericCardViewModelType: class { associatedtype SectionViewModelType: GenericCardSectionViewModelType var sectionViewModels: [SectionViewModelType] { get set }
}
GenericCard SectionViewModelType
protocol GenericCardSectionViewModelType: class { associatedtype ItemViewType: UIView, CreatableViewModeled, StaticSize associatedtype SectionHeaderViewType: UIView, CreatableViewModeled,
StaticHeight var itemViewModels: [ItemViewType.ViewModel] { get set } var sectionHeaderViewModel: SectionHeaderViewType.ViewModel? { get
set } }
第31页
ViewModel
GenericCard ViewModel
class GenericCardViewModel<SectionViewModelType: GenericCardSectionViewModelType>: GenericCardViewModelType {
var sectionViewModels: [SectionViewModelType] init(sectionViewModels: [SectionViewModelType]) {
self.sectionViewModels = sectionViewModels }
}
第32页
GenericCard SectionViewModel
class GenericCardSectionViewModel <ItemViewType: UIView, SectionHeaderViewType: UIView where ItemViewType: protocol<CreatableViewModeled, StaticSize>, SectionHeaderViewType: protocol<CreatableViewModeled, StaticHeight> > : GenericCardSectionViewModelType { var itemViewModels: [ItemViewType.ViewModel] var sectionHeaderViewModel: SectionHeaderViewType.ViewModel? init(itemViewModels: [ItemViewType.ViewModel], sectionHeaderViewModel:
SectionHeaderViewType.ViewModel? = nil) { self.itemViewModels = itemViewModels self.sectionHeaderViewModel = sectionHeaderViewModel
} }
第33页
GenericCard View
final class GenericCardView<ViewModelType: GenericCardViewModelType> : UIView {
weak var delegate: GenericCardViewDelegate?
init(viewModel: ViewModelType) { super.init(frame: CGRect.zero) var subviews = [UIView]() for (index, svm) in viewModel.sectionViewModels.enumerate() { let sectionView =
GenericCardSectionView<ViewModelType.SectionViewModelType>(viewModel: svm, delegate: self, index: index)
subviews.append(sectionView) } let list = StackLayoutView(arrangedSubviews: subviews) addSubview(list) LayoutUtils.setEdgesFor(list)
}
}
第34页
final class GenericCardSectionView<ViewModelType: GenericCardSectionViewModelType>: UIView {
GenericCard
typealias ItemViewType = ViewModelType.ItemViewType
SectionView
typealias SectionHeaderViewType = ViewModelType.SectionHeaderViewType
init(viewModel: ViewModelType, delegate: GenericCardSectionContentViewDelegate, index: Int) {
super.init(frame: CGRectZero) var subviews = [UIView]()
if let headerViewModel = viewModel.sectionHeaderViewModel {
let headerView = SectionHeaderViewType()
headerView.bindDataWithViewModel(headerViewModel)
SectionHeader
LayoutUtils.setHeightFor(headerView, height: SectionHeaderViewType.staticHeight)
View
subviews.append(headerView)
}
let contentView = GenericCardSectionContentView<ItemViewType>(itemViewModels: viewModel.itemViewModels)
contentView.delegate = delegate contentView.tag = index subviews.append(contentView)
GenericCard SectionContentView
let list = StackLayoutView(arrangedSubviews: subviews) addSubview(list) LayoutUtils.setEdgesFor(list) }
}
第35页
GenericCard SectionContentView
final class GenericCardSectionContentView<ItemViewType: UIView where ItemViewType: protocol<CreatableViewModeled, StaticSize>>: UIView {
weak var delegate: GenericCardSectionContentViewDelegate? typealias ItemViewModel = ItemViewType.ViewModel private let itemViewModels: [ItemViewModel] init(itemViewModels: [ItemViewModel]) {
self.itemViewModels = itemViewModels super.init(frame: CGRect.zero) setupUserInterface() } }
第36页
private func setupUserInterface() {
let list = itemViewModels.split(step:5).map { vmsSlice in
Array(vmsSlice).map { vm in let itemView = ItemViewType() itemView.bindDataWithViewModel(vm) itemView.tag == index index += 1 return itemView.touchableWrapped(self, action:
#selector(itemViewPressed)) } .stackLayoutWrapped { config in config.axis = .Horizontal config.alongAlignment = .Fill config.distribution = .EqualSpacing } } .stackLayoutWrapped()
addSubview(list) LayoutUtils.setEdgesFor(list)
}
第37页
@objc private func itemViewPressed(view: UIView) { delegate?.cardSectionContentView(self, didTapItemAtIndex: view.tag)
}
protocol GenericCardSectionContentViewDelegate: class { func cardSectionContentView<ContentViewType: UIView>(contentView:
ContentViewType, didTapItemAtIndex index: Int) }
objc UIView
第38页
protocol GenericCardViewDelegate: class { func cardView<CardViewType: UIView>(contentView: CardViewType,
didTapItemAtIndexPath indexPath: NSIndexPath) }
extension GenericCardView: GenericCardSectionContentViewDelegate { func cardSectionContentView<T: UIView>(contentView: T,
didTapItemAtIndex index: Int) { let indexPath = NSIndexPath(forRow: index, inSection:
contentView.tag) delegate?.cardView(self, didTapItemAtIndexPath: indexPath)
} }
第40页
ItemView HeaderView
final class DemoCardItemView: UIView, CreatableViewModeled, StaticSize {
init() {……} static var staticSize: CGSize = CGSize(width: 66, height: 66) struct ViewModel {
let text: String let textColor: UIColor let image: UIImage } func bindDataWithViewModel(viewModel: ViewModel) { label.text = viewModel.text label.textColor = viewModel.textColor imageView.image = viewModel.image } …… } final class DemoCardChapterView: LabelWrapper, CreatableViewModeled, StaticHeight { …… }
第41页
ViewModel
typealias DemoCardSectionViewModel = GenericCardSectionViewModel<DemoCardItemView, DemoCardChapterView> typealias DemoCardViewModel = GenericCardViewModel<DemoCardSectionViewModel>
typealias DemoCardView = GenericCardView<DemoCardViewModel>
func makeViewModel(data: …) -> DemoCardViewModel { // viewModel
}
第42页
let cardView = DemoCardView(viewModel: makeViewModel()) cardView.delegate = self let scrollableCardView = cardView.scrollableWrapped() view.addSubview(scrollableCardView)
第43页
• GenericCardView ItemView HeaderView
• ItemView HeaderView
ViewModel
CardViewModel
• CardViewModel CardView
第45页
One More Thing
第46页
GenericTableViewModeled GenericCollectionViewModeled
第47页
delegate dataSource
/// Generic collection view modeled. protocol GenericCollectionViewModeled: ViewModeled {
/// View model type. associatedtype ViewModel: GenericCollectionViewModelType /// Collection view model. var viewModel: ViewModel? { get set } /// Cell identifier, for generating and getting cell. var cellIdentifier: String { get } /// Collection view. var collectionView: UICollectionView { get } /// Return number of sections from view model func numberOfSections() -> Int /// Return number of items in a `section` from view model func numberOfItemsInSection(section: Int) -> Int /// Return section view model in given `section` func sectionViewModelInSection(section: Int) -> ViewModel.SectionViewModel? /// Return cell view model at `indexPath` func cellViewModelAtIndexPath(indexPath: NSIndexPath) -> ViewModel.SectionViewModel.CellView.ViewModel? /// Delete cell view model at `indexPath` func deleteCellViewModelAtIndexPath(indexPath: NSIndexPath) -> Bool /// Generate/get cell at `indexPath` func dequeueReusableCellAndBindDataForIndexPath(indexPath: NSIndexPath) -> GenericCollectionCell<ViewModel.SectionViewModel.CellView>
extension GenericCollectionViewModeled { /// Default cell identifier var cellIdentifier: String { return "CellIdentifier" } /// Default implementation of binding data with view mo /// /// - parameter viewModel: The view model func bindDataWithViewModel(viewModel: ViewModel) { self.viewModel = viewModel collectionView.reloadData() } /// Default implementation of getting section number fo func numberOfSections() -> Int { return viewModel?.sectionViewModels.count jQuery1102019649039221848152_1476070498175 0 } /// Default implementation of getting items number for func numberOfItemsInSection(section: Int) -> Int { return viewModel?.sectionViewModels.elementAtIndex(s } /// Default implementation of getting section view mode func sectionViewModelInSection(section: Int) -> ViewMode return viewModel?.sectionViewModels.elementAtIndex(s } /// Default implementation of getting cell view model f func cellViewModelAtIndexPath(indexPath: NSIndexPath) -> return viewModel?.sectionViewModels.elementAtIndex(i .cellViewModels.elementAtIndex(inde }
第48页
CellViewType HeaderViewType, FooterViewType
final class AnswerCardView: CollectionViewContainer, GenericCollectionViewModeled {
typealias CellView = ItemView typealias SectionHeaderView = ChapterView typealias ViewModel = GenericSectionGroupViewModel<GenericSectionViewModel<CellView, SectionHeaderView, VoidView>> … }
第49页
View
extension GenericCollectionViewModeled where ViewModel.SectionViewModel.CellView: StaticSize {
/// Static item size var staticItemSize: CGSize {
return ViewModel.SectionViewModel.CellView.staticSize } /// Return size for item at 'indexPath'. func sizeForItemAtIndexPath(indexPath: NSIndexPath) -> CGSize {
return staticItemSize } } extension GenericCollectionViewModeled where ViewModel.SectionViewModel.CellView: ViewModeledSize { /// Return size for item at 'indexPath'. func sizeForItemAtIndexPath(indexPath: NSIndexPath) -> CGSize {
guard let cvm = cellViewModelAtIndexPath(indexPath) else { return CGSize.zero }
return ViewModel.SectionViewModel.CellView.sizeWithViewModel(cvm) } }
第51页
Thanks Q&A
We’re Hiring! lancy@fenbi.com