Skip to content

Commit

Permalink
39
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Nov 26, 2018
1 parent b6dccc4 commit f414a66
Show file tree
Hide file tree
Showing 18 changed files with 1,625 additions and 0 deletions.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import Foundation
import UIKit
import Overture

final class SubscribeCalloutCell: UITableViewCell {
private let bodyLabel = UILabel()
private let buttonsStackView = UIStackView()
private let cardView = UIView()
private let loginButton = UIButton()
private let orLabel = UILabel()
private let rootStackView = UIStackView()
private let subscribeButton = UIButton()
private let titleLabel = UILabel()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

self.selectionStyle = .none
self.contentView.layoutMargins = .init(top: .pf_grid(6), left: .pf_grid(6), bottom: .pf_grid(6), right: .pf_grid(6))

self.titleLabel.text = "Subscribe to Point-Free"
self.titleLabel.font = UIFont.preferredFont(forTextStyle: .title3)

self.bodyLabel.text = "👋 Hey there! See anything you like? You may be interested in subscribing so that you get access to these episodes and all future ones."
self.bodyLabel.numberOfLines = 0
self.bodyLabel.font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.subheadline)

self.cardView.backgroundColor = UIColor(white: 0.96, alpha: 1.0)
with(self.cardView, generousMargins)
self.cardView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.cardView)

self.rootStackView.alignment = .leading
with(self.rootStackView, baseStackViewStyle)
self.rootStackView.addArrangedSubview(self.titleLabel)
self.rootStackView.addArrangedSubview(self.bodyLabel)
self.rootStackView.addArrangedSubview(self.buttonsStackView)
self.contentView.addSubview(self.rootStackView)

self.orLabel.text = "or"
self.orLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)

self.subscribeButton.setTitle("See subscription options", for: .normal)
with(self.subscribeButton, primaryButtonStyle)

self.loginButton.setTitle("Login", for: .normal)
with(self.loginButton, secondaryTextButtonStyle)

self.buttonsStackView.spacing = .pf_grid(2)
self.buttonsStackView.alignment = .firstBaseline
self.buttonsStackView.addArrangedSubview(self.subscribeButton)
self.buttonsStackView.addArrangedSubview(self.orLabel)
self.buttonsStackView.addArrangedSubview(self.loginButton)

NSLayoutConstraint.activate([
self.rootStackView.leadingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.leadingAnchor),
self.rootStackView.topAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.topAnchor),
self.rootStackView.trailingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.trailingAnchor),
self.rootStackView.bottomAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.bottomAnchor),

self.cardView.leadingAnchor.constraint(equalTo: self.rootStackView.leadingAnchor),
self.cardView.topAnchor.constraint(equalTo: self.rootStackView.topAnchor),
self.cardView.trailingAnchor.constraint(equalTo: self.rootStackView.trailingAnchor),
self.cardView.bottomAnchor.constraint(equalTo: self.rootStackView.bottomAnchor),
])
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

final class EpisodeCell: UITableViewCell {
private let blurbLabel = UILabel()
private let contentStackView = UIStackView()
private let posterImageView = UIImageView()
private let rootStackView = UIStackView()
private let sequenceAndDateLabel = UILabel()
private let subscriberOnlyLabel = UILabel()
private lazy var subscriberOnlyLabelWrapper = with(
wrapView(
padding: UIEdgeInsets(
top: .pf_grid(1),
left: .pf_grid(2),
bottom: .pf_grid(1),
right: .pf_grid(2)
)
)(self.subscriberOnlyLabel),
concat(
autoLayoutStyle,
baseRoundedStyle,
mut(\UIView.backgroundColor, UIColor(white: 0, alpha: 0.3))
)
)

private let titleLabel = UILabel()
private let watchNowButton = UIButton()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

self.blurbLabel.numberOfLines = 0
self.blurbLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)

with(self.contentStackView, concat(
baseStackViewStyle,
mut(\.layoutMargins.bottom, .pf_grid(8))
))
self.contentStackView.alignment = .leading
self.contentStackView.addArrangedSubview(self.sequenceAndDateLabel)
self.contentStackView.addArrangedSubview(self.titleLabel)
self.contentStackView.addArrangedSubview(self.blurbLabel)
self.contentStackView.addArrangedSubview(self.watchNowButton)

with(self.rootStackView, concat(
autoLayoutStyle,
verticalStackView
))
self.rootStackView.addArrangedSubview(self.posterImageView)
self.rootStackView.addArrangedSubview(self.contentStackView)

with(self.sequenceAndDateLabel, smallCapsLabelStyle)

self.titleLabel.font = UIFont.preferredFont(forTextStyle: .title2)

self.watchNowButton.setTitle("Watch episode →", for: .normal)
with(self.watchNowButton, primaryTextButtonStyle)

self.subscriberOnlyLabel.text = "Subscriber Only"
with(self.subscriberOnlyLabel, concat(
smallCapsLabelStyle,
mut(\.textColor, .white)
))

self.contentView.addSubview(self.rootStackView)
// self.contentView.addSubview(self.subscriberOnlyLabelWrapper)

NSLayoutConstraint.activate([
self.rootStackView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
self.rootStackView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
self.rootStackView.topAnchor.constraint(equalTo: self.contentView.topAnchor),
self.rootStackView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),

self.posterImageView.widthAnchor.constraint(equalTo: self.posterImageView.heightAnchor, multiplier: 16/9),

// self.subscriberOnlyLabelWrapper.topAnchor.constraint(equalTo: self.posterImageView.topAnchor, constant: .pf_grid(3)),
// self.subscriberOnlyLabelWrapper.trailingAnchor.constraint(equalTo: self.posterImageView.trailingAnchor, constant: -.pf_grid(6)),
])
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func configure(with episode: Episode) {
self.posterImageView.backgroundColor = episode.color
self.titleLabel.text = episode.title
self.blurbLabel.text = episode.blurb
let formattedDate = episodeDateFormatter.string(from: episode.publishedAt)
self.sequenceAndDateLabel.text = "#\(episode.sequence)\(formattedDate)"
self.subscriberOnlyLabelWrapper.isHidden = !episode.subscriberOnly

URLSession.shared.dataTask(with: URL(string: episode.posterImageUrl)!) { data, _, _ in
DispatchQueue.main.async { self.posterImageView.image = data.flatMap(UIImage.init(data:)) }
}.resume()
}
}

public final class EpisodeListViewController: UITableViewController {
let episodes: [Episode]

init<Episodes: Collection>(episodes: Episodes) where Episodes.Element == Episode {
self.episodes = Array(episodes)
super.init(nibName: nil, bundle: nil)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override public func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 400
self.tableView.rowHeight = UITableView.automaticDimension
}

override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
return SubscribeCalloutCell(style: .default, reuseIdentifier: nil)
}

let cell = EpisodeCell(style: .default, reuseIdentifier: nil)
cell.configure(with: self.episodes[indexPath.row - 1])
return cell
}

override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.episodes.count + 1
}
}
22 changes: 22 additions & 0 deletions 0039-witness-oriented-library-design/PointFreeFramework/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Foundation
import UIKit

public struct Episode {
public let blurb: String
public let color: UIColor
public let posterImageUrl: String
public let publishedAt: Date
public let sequence: Int
public let subscriberOnly: Bool
public let title: String
}

public let episodes: [Episode] = [
// .init(
// blurb: """
//What does the Swift type system have to do with algebra? A lot! We’ll begin to explore this correspondence and see how it can help us create type-safe data structures that can catch runtime errors at compile time.
//""",
// color: .pf_yellow,
// posterImageUrl: "https://d1hf1soyumxcgv.cloudfront.net/0004-adt/0004-poster.jpg",
// publishedAt: Date(timeIntervalSince1970: 1_519_045_951),
// sequence: 4,
// subscriberOnly: true,
// title: "Algebraic Data Types"
// ),
.init(
blurb: """
We bring tools from previous episodes down to earth and apply them to an everyday task: UIKit styling. Plain functions unlock worlds of composability and reusability in styling of UI components. Have we finally solved the styling problem?
""",
color: .pf_purple,
posterImageUrl: "https://d1hf1soyumxcgv.cloudfront.net/0003-styling-with-functions/0003-poster.jpg",
publishedAt: Date(timeIntervalSince1970: 1_519_045_951),
sequence: 3,
subscriberOnly: false,
title: "UIKit Styling with Functions"
),
.init(
blurb: """
Side effects: can’t live with ’em; can’t write a program without ’em. Let’s explore a few kinds of side effects we encounter every day, why they make code difficult to reason about and test, and how we can control them without losing composition.
""",
color: .pf_blue,
posterImageUrl: "https://d1hf1soyumxcgv.cloudfront.net/0002-side-effects/0002-poster.jpg",
publishedAt: Date(timeIntervalSince1970: 1_517_811_069),
sequence: 2,
subscriberOnly: false,
title: "Side Effects"
),
.init(
blurb: """
Our first episode is all about functions! We talk a bit about what makes functions special, contrasting them with the way we usually write code, and have some exploratory discussions about operators and composition.
""",
color: .pf_green,
posterImageUrl: "https://d1hf1soyumxcgv.cloudfront.net/0001-functions/0001-poster.jpg",
publishedAt: Date(timeIntervalSince1970: 1_517_206_269),
sequence: 1,
subscriberOnly: false,
title: "Functions"
),
.init(
blurb: """
Point-Free is here, bringing you videos covering functional programming concepts using the Swift language. Take a moment to hear from the hosts about what to expect from this new series.
""",
color: .pf_yellow,
posterImageUrl: "https://d1hf1soyumxcgv.cloudfront.net/0000-introduction/0000-poster.jpg",
publishedAt: Date(timeIntervalSince1970: 1_517_206_269),
sequence: 0,
subscriberOnly: false,
title: "We launched!"
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#import <UIKit/UIKit.h>
FOUNDATION_EXPORT double PointFreeFrameworkVersionNumber;
FOUNDATION_EXPORT const unsigned char PointFreeFrameworkVersionString[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Foundation
import UIKit
import Overture

extension CGFloat {
static func pf_grid(_ n: Int) -> CGFloat {
return CGFloat(n) * 4
}
}

let generousMargins =
mut(\UIView.layoutMargins, .init(top: .pf_grid(6), left: .pf_grid(6), bottom: .pf_grid(6), right: .pf_grid(6)))

let autoLayoutStyle = mut(\UIView.translatesAutoresizingMaskIntoConstraints, false)

let verticalStackView = mut(\UIStackView.axis, .vertical)

let baseStackViewStyle = concat(
generousMargins,
mut(\UIStackView.spacing, .pf_grid(3)),
verticalStackView,
mut(\.isLayoutMarginsRelativeArrangement, true),
autoLayoutStyle
)

let bolded: (inout UIFont) -> Void = { $0 = $0.bolded }

let baseTextButtonStyle = concat(
mut(\UIButton.titleLabel!.font, UIFont.preferredFont(forTextStyle: .subheadline)),
mver(\UIButton.titleLabel!.font!, bolded)
)

extension UIButton {
var normalTitleColor: UIColor? {
get { return self.titleColor(for: .normal) }
set { self.setTitleColor(newValue, for: .normal) }
}
}

let secondaryTextButtonStyle = concat(
baseTextButtonStyle,
mut(\.normalTitleColor, .black)
)

let primaryTextButtonStyle = concat(
baseTextButtonStyle,
mut(\.normalTitleColor, .pf_purple)
)

let baseButtonStyle = concat(
baseTextButtonStyle,
mut(\.contentEdgeInsets, .init(top: .pf_grid(2), left: .pf_grid(4), bottom: .pf_grid(2), right: .pf_grid(4)))
)

func roundedStyle(cornerRadius: CGFloat) -> (UIView) -> Void {
return concat(
mut(\.layer.cornerRadius, cornerRadius),
mut(\.layer.masksToBounds, true)
)
}

let baseRoundedStyle = roundedStyle(cornerRadius: 6)

let baseFilledButtonStyle = concat(
baseButtonStyle,
baseRoundedStyle
)

extension UIButton {
var normalBackgroundImage: UIImage? {
get { return self.backgroundImage(for: .normal) }
set { self.setBackgroundImage(newValue, for: .normal) }
}
}

let primaryButtonStyle = concat(
baseFilledButtonStyle,
mut(\.normalBackgroundImage, .from(color: .pf_purple)),
mut(\.normalTitleColor, .white)
)

let smallCapsLabelStyle = mut(\UILabel.font, UIFont.preferredFont(forTextStyle: .caption1).smallCaps)
Loading

0 comments on commit f414a66

Please sign in to comment.