Skip to content

Commit 4b7f27d

Browse files
authored
Example app. (#4)
1 parent 98575e8 commit 4b7f27d

File tree

13 files changed

+840
-1
lines changed

13 files changed

+840
-1
lines changed

FlexibleDiff.xcodeproj/project.pbxproj

Lines changed: 211 additions & 1 deletion
Large diffs are not rendered by default.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ github "RACCommunity/FlexibleDiff"
6161
pod "FlexibleDiff"
6262
```
6363

64+
## The Example App
65+
The Xcode workspace includes an example app using FlexibleDiff. Be sure to run `git submodule update --init` to fetch the dependencies before building the app target in Xcode.
66+
6467
## Note on the algorithm
6568
The implementation is evolved from the popular O(n) diff algorithm by Paul
6669
Heckel, in [his 1978 paper "A technique for isolating differences between files"](https://dl.acm.org/citation.cfm?id=359467).

WordList/AppDelegate.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import UIKit
2+
3+
@UIApplicationMain
4+
class AppDelegate: UIResponder, UIApplicationDelegate {
5+
var window: UIWindow?
6+
7+
func application(
8+
_ application: UIApplication,
9+
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
10+
) -> Bool {
11+
let window = UIWindow()
12+
self.window = window
13+
14+
window.rootViewController = RootBuilder().make()
15+
window.makeKeyAndVisible()
16+
return true
17+
}
18+
}
19+
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "iphone",
5+
"size" : "20x20",
6+
"scale" : "2x"
7+
},
8+
{
9+
"idiom" : "iphone",
10+
"size" : "20x20",
11+
"scale" : "3x"
12+
},
13+
{
14+
"idiom" : "iphone",
15+
"size" : "29x29",
16+
"scale" : "2x"
17+
},
18+
{
19+
"idiom" : "iphone",
20+
"size" : "29x29",
21+
"scale" : "3x"
22+
},
23+
{
24+
"idiom" : "iphone",
25+
"size" : "40x40",
26+
"scale" : "2x"
27+
},
28+
{
29+
"idiom" : "iphone",
30+
"size" : "40x40",
31+
"scale" : "3x"
32+
},
33+
{
34+
"idiom" : "iphone",
35+
"size" : "60x60",
36+
"scale" : "2x"
37+
},
38+
{
39+
"idiom" : "iphone",
40+
"size" : "60x60",
41+
"scale" : "3x"
42+
},
43+
{
44+
"idiom" : "ipad",
45+
"size" : "20x20",
46+
"scale" : "1x"
47+
},
48+
{
49+
"idiom" : "ipad",
50+
"size" : "20x20",
51+
"scale" : "2x"
52+
},
53+
{
54+
"idiom" : "ipad",
55+
"size" : "29x29",
56+
"scale" : "1x"
57+
},
58+
{
59+
"idiom" : "ipad",
60+
"size" : "29x29",
61+
"scale" : "2x"
62+
},
63+
{
64+
"idiom" : "ipad",
65+
"size" : "40x40",
66+
"scale" : "1x"
67+
},
68+
{
69+
"idiom" : "ipad",
70+
"size" : "40x40",
71+
"scale" : "2x"
72+
},
73+
{
74+
"idiom" : "ipad",
75+
"size" : "76x76",
76+
"scale" : "1x"
77+
},
78+
{
79+
"idiom" : "ipad",
80+
"size" : "76x76",
81+
"scale" : "2x"
82+
},
83+
{
84+
"idiom" : "ipad",
85+
"size" : "83.5x83.5",
86+
"scale" : "2x"
87+
}
88+
],
89+
"info" : {
90+
"version" : 1,
91+
"author" : "xcode"
92+
}
93+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3+
<dependencies>
4+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
5+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
6+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
7+
</dependencies>
8+
<scenes>
9+
<!--View Controller-->
10+
<scene sceneID="EHf-IW-A2E">
11+
<objects>
12+
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
13+
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
14+
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
15+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
16+
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
17+
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
18+
</view>
19+
</viewController>
20+
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
21+
</objects>
22+
<point key="canvasLocation" x="53" y="375"/>
23+
</scene>
24+
</scenes>
25+
</document>

WordList/Info.plist

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>$(DEVELOPMENT_LANGUAGE)</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>APPL</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleVersion</key>
20+
<string>1</string>
21+
<key>LSRequiresIPhoneOS</key>
22+
<true/>
23+
<key>UILaunchStoryboardName</key>
24+
<string>LaunchScreen</string>
25+
<key>UIRequiredDeviceCapabilities</key>
26+
<array>
27+
<string>armv7</string>
28+
</array>
29+
<key>UISupportedInterfaceOrientations</key>
30+
<array>
31+
<string>UIInterfaceOrientationPortrait</string>
32+
</array>
33+
<key>UISupportedInterfaceOrientations~ipad</key>
34+
<array>
35+
<string>UIInterfaceOrientationPortrait</string>
36+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
37+
<string>UIInterfaceOrientationLandscapeLeft</string>
38+
<string>UIInterfaceOrientationLandscapeRight</string>
39+
</array>
40+
</dict>
41+
</plist>

WordList/RootBuilder.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import UIKit
2+
3+
struct RootBuilder {
4+
func make() -> UIViewController {
5+
let navigation = UINavigationController()
6+
let wordList = WordListBuilder().make(navigation: navigation)
7+
navigation.setViewControllers([wordList], animated: false)
8+
navigation.navigationBar.prefersLargeTitles = true
9+
10+
return navigation
11+
}
12+
}

WordList/Word.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Foundation
2+
3+
struct Word: Equatable {
4+
let identifier: UUID
5+
let word: String
6+
7+
init(word: String) {
8+
self.identifier = UUID()
9+
self.word = word
10+
}
11+
12+
init(identifier: UUID, word: String) {
13+
self.identifier = identifier
14+
self.word = word
15+
}
16+
17+
func updating(_ word: String) -> Word {
18+
return Word(identifier: self.identifier, word: word)
19+
}
20+
21+
static func == (left: Word, right: Word) -> Bool {
22+
return left.identifier == right.identifier && left.word == right.word
23+
}
24+
}

WordList/WordCell.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import UIKit
2+
3+
class WordCell: UITableViewCell {
4+
private let wordLabel: UILabel
5+
private let uuidLabel: UILabel
6+
7+
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
8+
wordLabel = UILabel()
9+
uuidLabel = UILabel()
10+
11+
super.init(style: style, reuseIdentifier: reuseIdentifier)
12+
setupViews()
13+
}
14+
15+
@available(*, unavailable)
16+
required init?(coder aDecoder: NSCoder) {
17+
fatalError("init(coder:) has not been implemented")
18+
}
19+
20+
func configure(_ word: Word) {
21+
wordLabel.text = word.word
22+
uuidLabel.text = word.identifier.uuidString
23+
}
24+
25+
private func setupViews() {
26+
wordLabel.translatesAutoresizingMaskIntoConstraints = false
27+
uuidLabel.translatesAutoresizingMaskIntoConstraints = false
28+
29+
contentView.addSubview(wordLabel)
30+
contentView.addSubview(uuidLabel)
31+
32+
let constraints = [
33+
wordLabel.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
34+
wordLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
35+
wordLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
36+
wordLabel.bottomAnchor.constraint(equalTo: uuidLabel.topAnchor, constant: -2),
37+
uuidLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
38+
uuidLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
39+
uuidLabel.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
40+
]
41+
42+
constraints.forEach { $0.priority = UILayoutPriority.defaultHigh }
43+
NSLayoutConstraint.activate(constraints)
44+
45+
wordLabel.textAlignment = .natural
46+
uuidLabel.textAlignment = .natural
47+
48+
wordLabel.font = .preferredFont(forTextStyle: .headline)
49+
uuidLabel.font = .preferredFont(forTextStyle: .caption2)
50+
uuidLabel.textColor = .gray
51+
}
52+
}

WordList/WordListBuilder.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import UIKit
2+
3+
enum EditorKind {
4+
case add
5+
case edit(original: String)
6+
}
7+
8+
enum ListAction {
9+
case add
10+
case shuffle
11+
case xchgShuffle
12+
}
13+
14+
protocol WordListChildBuilders {
15+
func makeEditor(_ kind: EditorKind, _ response: @escaping (String?) -> Void) -> UIViewController
16+
func makeActions(_ response: @escaping (ListAction) -> Void) -> UIViewController
17+
}
18+
19+
struct WordListBuilder: WordListChildBuilders {
20+
func make(navigation: UINavigationController) -> UIViewController {
21+
let flowController = WordListFlowController(navigation: navigation, builders: self)
22+
let viewModel = WordListViewModel(routing: flowController)
23+
let viewController = WordListViewController(viewModel: viewModel)
24+
return viewController
25+
}
26+
27+
func makeEditor(_ kind: EditorKind, _ response: @escaping (String?) -> Void) -> UIViewController {
28+
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
29+
30+
switch kind {
31+
case .add:
32+
alert.title = "Add new words"
33+
alert.message = "Separate words with comma or space."
34+
alert.addTextField { _ in }
35+
36+
case let .edit(original):
37+
alert.title = "Enter the replacement word"
38+
alert.addTextField { $0.text = original }
39+
}
40+
41+
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in response(nil) })
42+
alert.addAction(cancel)
43+
44+
let commit = UIAlertAction(title: "Confirm",
45+
style: .default,
46+
handler: { _ in response(alert.textFields![0].text ?? "") })
47+
alert.addAction(commit)
48+
49+
alert.preferredAction = commit
50+
51+
return alert
52+
}
53+
54+
func makeActions(_ response: @escaping (ListAction) -> Void) -> UIViewController {
55+
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
56+
57+
let create = UIAlertAction(title: "Add", style: .default, handler: { _ in response(.add) })
58+
actionSheet.addAction(create)
59+
60+
let shuffle = UIAlertAction(title: "Shuffle", style: .default, handler: { _ in response(.shuffle) })
61+
actionSheet.addAction(shuffle)
62+
63+
let xchgShuffle = UIAlertAction(title: "Exchange and Shuffle", style: .default, handler: { _ in response(.xchgShuffle) })
64+
actionSheet.addAction(xchgShuffle)
65+
66+
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in })
67+
actionSheet.addAction(cancel)
68+
69+
return actionSheet
70+
}
71+
}

0 commit comments

Comments
 (0)