├── MVVMPlayground
├── .gitignore
├── MVVMPlayground.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── MVVMPlayground.xcworkspace
│ └── contents.xcworkspacedata
├── MVVMPlayground
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── Model
│ │ └── photo.swift
│ ├── Module
│ │ ├── PhotoDetail
│ │ │ └── PhotoDetailViewController.swift
│ │ └── PhotoList
│ │ │ ├── PhotoListViewController.swift
│ │ │ └── PhotoListViewModel.swift
│ └── Service
│ │ ├── APIService.swift
│ │ └── content.json
├── MVVMPlaygroundTests
│ ├── APIServiceTests.swift
│ ├── Info.plist
│ └── PhotoListViewModelTests.swift
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ └── project.pbxproj
│ ├── SDWebImage
│ │ ├── LICENSE
│ │ ├── README.md
│ │ └── SDWebImage
│ │ │ ├── NSData+ImageContentType.h
│ │ │ ├── NSData+ImageContentType.m
│ │ │ ├── NSImage+WebCache.h
│ │ │ ├── NSImage+WebCache.m
│ │ │ ├── SDImageCache.h
│ │ │ ├── SDImageCache.m
│ │ │ ├── SDImageCacheConfig.h
│ │ │ ├── SDImageCacheConfig.m
│ │ │ ├── SDWebImageCompat.h
│ │ │ ├── SDWebImageCompat.m
│ │ │ ├── SDWebImageDecoder.h
│ │ │ ├── SDWebImageDecoder.m
│ │ │ ├── SDWebImageDownloader.h
│ │ │ ├── SDWebImageDownloader.m
│ │ │ ├── SDWebImageDownloaderOperation.h
│ │ │ ├── SDWebImageDownloaderOperation.m
│ │ │ ├── SDWebImageManager.h
│ │ │ ├── SDWebImageManager.m
│ │ │ ├── SDWebImageOperation.h
│ │ │ ├── SDWebImagePrefetcher.h
│ │ │ ├── SDWebImagePrefetcher.m
│ │ │ ├── UIButton+WebCache.h
│ │ │ ├── UIButton+WebCache.m
│ │ │ ├── UIImage+GIF.h
│ │ │ ├── UIImage+GIF.m
│ │ │ ├── UIImage+MultiFormat.h
│ │ │ ├── UIImage+MultiFormat.m
│ │ │ ├── UIImageView+HighlightedWebCache.h
│ │ │ ├── UIImageView+HighlightedWebCache.m
│ │ │ ├── UIImageView+WebCache.h
│ │ │ ├── UIImageView+WebCache.m
│ │ │ ├── UIView+WebCache.h
│ │ │ ├── UIView+WebCache.m
│ │ │ ├── UIView+WebCacheOperation.h
│ │ │ └── UIView+WebCacheOperation.m
│ └── Target Support Files
│ │ ├── Pods-MVVMPlayground
│ │ ├── Info.plist
│ │ ├── Pods-MVVMPlayground-acknowledgements.markdown
│ │ ├── Pods-MVVMPlayground-acknowledgements.plist
│ │ ├── Pods-MVVMPlayground-dummy.m
│ │ ├── Pods-MVVMPlayground-frameworks.sh
│ │ ├── Pods-MVVMPlayground-resources.sh
│ │ ├── Pods-MVVMPlayground-umbrella.h
│ │ ├── Pods-MVVMPlayground.debug.xcconfig
│ │ ├── Pods-MVVMPlayground.modulemap
│ │ └── Pods-MVVMPlayground.release.xcconfig
│ │ ├── Pods-MVVMPlaygroundTests
│ │ ├── Info.plist
│ │ ├── Pods-MVVMPlaygroundTests-acknowledgements.markdown
│ │ ├── Pods-MVVMPlaygroundTests-acknowledgements.plist
│ │ ├── Pods-MVVMPlaygroundTests-dummy.m
│ │ ├── Pods-MVVMPlaygroundTests-frameworks.sh
│ │ ├── Pods-MVVMPlaygroundTests-resources.sh
│ │ ├── Pods-MVVMPlaygroundTests-umbrella.h
│ │ ├── Pods-MVVMPlaygroundTests.debug.xcconfig
│ │ ├── Pods-MVVMPlaygroundTests.modulemap
│ │ └── Pods-MVVMPlaygroundTests.release.xcconfig
│ │ └── SDWebImage
│ │ ├── Info.plist
│ │ ├── SDWebImage-dummy.m
│ │ ├── SDWebImage-prefix.pch
│ │ ├── SDWebImage-umbrella.h
│ │ ├── SDWebImage.modulemap
│ │ └── SDWebImage.xcconfig
└── readme.md
├── NetworkingUnitTest.playground
├── Contents.swift
├── Resources
│ └── data.json
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
│ └── xcuserdata
│ │ └── neo.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── WorkspaceSettings.xcsettings
└── timeline.xctimeline
├── PersistentTodoList
├── PersistentTodoList.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata
│ │ │ └── neo.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── neo.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── PersistentTodoList.xcscheme
│ │ └── xcschememanagement.plist
├── PersistentTodoList
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── PersistentTodoList.xcdatamodeld
│ │ ├── .xccurrentversion
│ │ └── PersistentTodoList.xcdatamodel
│ │ │ └── contents
│ ├── Storage
│ │ └── ToDoStorageManager.swift
│ └── ViewController.swift
└── PersistentTodoListTests
│ ├── Info.plist
│ └── PersistentTodoListTests.swift
├── Sequence.playground
├── Contents.swift
├── contents.xcplayground
└── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ └── neo.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── UnitTestBasic.playground
├── Contents.swift
├── contents.xcplayground
└── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ └── neo.xcuserdatad
│ └── UserInterfaceState.xcuserstate
└── readme.md
/MVVMPlayground/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots
68 | fastlane/test_output
69 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 01/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // 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.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // 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.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
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 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Model/photo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Event.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 01/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | struct Photos: Codable {
11 | let photos: [Photo]
12 | }
13 |
14 | struct Photo: Codable {
15 | let id: Int
16 | let name: String
17 | let description: String?
18 | let created_at: Date
19 | let image_url: String
20 | let for_sale: Bool
21 | let camera: String?
22 | }
23 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Module/PhotoDetail/PhotoDetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoDetailViewController.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 03/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SDWebImage
11 |
12 | class PhotoDetailViewController: UIViewController {
13 |
14 | var imageUrl: String?
15 |
16 | @IBOutlet weak var imageView: UIImageView!
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | if let imageUrl = imageUrl {
21 | imageView.sd_setImage(with: URL(string: imageUrl)) { (image, error, type, url) in
22 |
23 | }
24 | }
25 |
26 | }
27 |
28 | override func didReceiveMemoryWarning() {
29 | super.didReceiveMemoryWarning()
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Module/PhotoList/PhotoListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventListViewController.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 01/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SDWebImage
11 |
12 | class PhotoListViewController: UIViewController {
13 |
14 | @IBOutlet weak var tableView: UITableView!
15 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
16 |
17 | lazy var viewModel: PhotoListViewModel = {
18 | return PhotoListViewModel()
19 | }()
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | // Init the static view
25 | initView()
26 |
27 | // init view model
28 | initVM()
29 |
30 | }
31 |
32 | func initView() {
33 | self.navigationItem.title = "Popular"
34 |
35 | tableView.estimatedRowHeight = 150
36 | tableView.rowHeight = UITableView.automaticDimension
37 | }
38 |
39 | func initVM() {
40 |
41 | // Naive binding
42 | viewModel.showAlertClosure = { [weak self] () in
43 | DispatchQueue.main.async {
44 | if let message = self?.viewModel.alertMessage {
45 | self?.showAlert( message )
46 | }
47 | }
48 | }
49 |
50 | viewModel.updateLoadingStatus = { [weak self] () in
51 | DispatchQueue.main.async {
52 | let isLoading = self?.viewModel.isLoading ?? false
53 | if isLoading {
54 | self?.activityIndicator.startAnimating()
55 | UIView.animate(withDuration: 0.2, animations: {
56 | self?.tableView.alpha = 0.0
57 | })
58 | }else {
59 | self?.activityIndicator.stopAnimating()
60 | UIView.animate(withDuration: 0.2, animations: {
61 | self?.tableView.alpha = 1.0
62 | })
63 | }
64 | }
65 | }
66 |
67 | viewModel.reloadTableViewClosure = { [weak self] () in
68 | DispatchQueue.main.async {
69 | self?.tableView.reloadData()
70 | }
71 | }
72 |
73 | viewModel.initFetch()
74 |
75 | }
76 |
77 | func showAlert( _ message: String ) {
78 | let alert = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
79 | alert.addAction( UIAlertAction(title: "Ok", style: .cancel, handler: nil))
80 | self.present(alert, animated: true, completion: nil)
81 | }
82 |
83 | override func didReceiveMemoryWarning() {
84 | super.didReceiveMemoryWarning()
85 | }
86 |
87 | }
88 |
89 | extension PhotoListViewController: UITableViewDelegate, UITableViewDataSource {
90 |
91 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
92 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "photoCellIdentifier", for: indexPath) as? PhotoListTableViewCell else {
93 | fatalError("Cell not exists in storyboard")
94 | }
95 |
96 | let cellVM = viewModel.getCellViewModel( at: indexPath )
97 | cell.photoListCellViewModel = cellVM
98 |
99 | return cell
100 | }
101 |
102 | func numberOfSections(in tableView: UITableView) -> Int {
103 | return 1
104 | }
105 |
106 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
107 | return viewModel.numberOfCells
108 | }
109 |
110 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
111 | return 150.0
112 | }
113 |
114 | func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
115 |
116 | self.viewModel.userPressed(at: indexPath)
117 | if viewModel.isAllowSegue {
118 | return indexPath
119 | }else {
120 | return nil
121 | }
122 | }
123 |
124 | }
125 |
126 | extension PhotoListViewController {
127 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
128 | if let vc = segue.destination as? PhotoDetailViewController,
129 | let photo = viewModel.selectedPhoto {
130 | vc.imageUrl = photo.image_url
131 | }
132 | }
133 | }
134 |
135 | class PhotoListTableViewCell: UITableViewCell {
136 | @IBOutlet weak var mainImageView: UIImageView!
137 | @IBOutlet weak var dateLabel: UILabel!
138 | @IBOutlet weak var descriptionLabel: UILabel!
139 | @IBOutlet weak var nameLabel: UILabel!
140 | @IBOutlet weak var descContainerHeightConstraint: NSLayoutConstraint!
141 | var photoListCellViewModel : PhotoListCellViewModel? {
142 | didSet {
143 | nameLabel.text = photoListCellViewModel?.titleText
144 | descriptionLabel.text = photoListCellViewModel?.descText
145 | mainImageView?.sd_setImage(with: URL( string: photoListCellViewModel?.imageUrl ?? "" ), completed: nil)
146 | dateLabel.text = photoListCellViewModel?.dateText
147 | }
148 | }
149 | }
150 |
151 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Module/PhotoList/PhotoListViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoListViewModel.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 03/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class PhotoListViewModel {
12 |
13 | let apiService: APIServiceProtocol
14 |
15 | private var photos: [Photo] = [Photo]()
16 |
17 | private var cellViewModels: [PhotoListCellViewModel] = [PhotoListCellViewModel]() {
18 | didSet {
19 | self.reloadTableViewClosure?()
20 | }
21 | }
22 |
23 | var isLoading: Bool = false {
24 | didSet {
25 | self.updateLoadingStatus?()
26 | }
27 | }
28 |
29 | var alertMessage: String? {
30 | didSet {
31 | self.showAlertClosure?()
32 | }
33 | }
34 |
35 | var numberOfCells: Int {
36 | return cellViewModels.count
37 | }
38 |
39 | var isAllowSegue: Bool = false
40 |
41 | var selectedPhoto: Photo?
42 |
43 | var reloadTableViewClosure: (()->())?
44 | var showAlertClosure: (()->())?
45 | var updateLoadingStatus: (()->())?
46 |
47 | init( apiService: APIServiceProtocol = APIService()) {
48 | self.apiService = apiService
49 | }
50 |
51 | func initFetch() {
52 | self.isLoading = true
53 | apiService.fetchPopularPhoto { [weak self] (success, photos, error) in
54 | self?.isLoading = false
55 | if let error = error {
56 | self?.alertMessage = error.rawValue
57 | } else {
58 | self?.processFetchedPhoto(photos: photos)
59 | }
60 | }
61 | }
62 |
63 | func getCellViewModel( at indexPath: IndexPath ) -> PhotoListCellViewModel {
64 | return cellViewModels[indexPath.row]
65 | }
66 |
67 | func createCellViewModel( photo: Photo ) -> PhotoListCellViewModel {
68 |
69 | //Wrap a description
70 | var descTextContainer: [String] = [String]()
71 | if let camera = photo.camera {
72 | descTextContainer.append(camera)
73 | }
74 | if let description = photo.description {
75 | descTextContainer.append( description )
76 | }
77 | let desc = descTextContainer.joined(separator: " - ")
78 |
79 | let dateFormatter = DateFormatter()
80 | dateFormatter.dateFormat = "yyyy-MM-dd"
81 |
82 | return PhotoListCellViewModel( titleText: photo.name,
83 | descText: desc,
84 | imageUrl: photo.image_url,
85 | dateText: dateFormatter.string(from: photo.created_at) )
86 | }
87 |
88 | private func processFetchedPhoto( photos: [Photo] ) {
89 | self.photos = photos // Cache
90 | var vms = [PhotoListCellViewModel]()
91 | for photo in photos {
92 | vms.append( createCellViewModel(photo: photo) )
93 | }
94 | self.cellViewModels = vms
95 | }
96 |
97 | }
98 |
99 | extension PhotoListViewModel {
100 | func userPressed( at indexPath: IndexPath ){
101 | let photo = self.photos[indexPath.row]
102 | if photo.for_sale {
103 | self.isAllowSegue = true
104 | self.selectedPhoto = photo
105 | }else {
106 | self.isAllowSegue = false
107 | self.selectedPhoto = nil
108 | self.alertMessage = "This item is not for sale"
109 | }
110 |
111 | }
112 | }
113 |
114 | struct PhotoListCellViewModel {
115 | let titleText: String
116 | let descText: String
117 | let imageUrl: String
118 | let dateText: String
119 | }
120 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlayground/Service/APIService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIService.swift
3 | // MVVMPlayground
4 | //
5 | // Created by Neo on 01/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum APIError: String, Error {
12 | case noNetwork = "No Network"
13 | case serverOverload = "Server is overloaded"
14 | case permissionDenied = "You don't have permission"
15 | }
16 |
17 | protocol APIServiceProtocol {
18 | func fetchPopularPhoto( complete: @escaping ( _ success: Bool, _ photos: [Photo], _ error: APIError? )->() )
19 | }
20 |
21 | class APIService: APIServiceProtocol {
22 | // Simulate a long waiting for fetching
23 | func fetchPopularPhoto( complete: @escaping ( _ success: Bool, _ photos: [Photo], _ error: APIError? )->() ) {
24 | DispatchQueue.global().async {
25 | sleep(3)
26 | let path = Bundle.main.path(forResource: "content", ofType: "json")!
27 | let data = try! Data(contentsOf: URL(fileURLWithPath: path))
28 | let decoder = JSONDecoder()
29 | decoder.dateDecodingStrategy = .iso8601
30 | let photos = try! decoder.decode(Photos.self, from: data)
31 | complete( true, photos.photos, nil )
32 | }
33 | }
34 |
35 |
36 |
37 | }
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlaygroundTests/APIServiceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIServiceTests.swift
3 | // MVVMPlaygroundTests
4 | //
5 | // Created by Neo on 01/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MVVMPlayground
11 |
12 | class APIServiceTests: XCTestCase {
13 |
14 | var sut: APIService?
15 |
16 | override func setUp() {
17 | super.setUp()
18 | sut = APIService()
19 | }
20 |
21 | override func tearDown() {
22 | sut = nil
23 | super.tearDown()
24 | }
25 |
26 | func test_fetch_popular_photos() {
27 |
28 | // Given A apiservice
29 | let sut = self.sut!
30 |
31 | // When fetch popular photo
32 | let expect = XCTestExpectation(description: "callback")
33 |
34 | sut.fetchPopularPhoto(complete: { (success, photos, error) in
35 | expect.fulfill()
36 | XCTAssertEqual( photos.count, 20)
37 | for photo in photos {
38 | XCTAssertNotNil(photo.id)
39 | }
40 |
41 | })
42 |
43 | wait(for: [expect], timeout: 3.1)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlaygroundTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MVVMPlayground/MVVMPlaygroundTests/PhotoListViewModelTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoListViewModelTests.swift
3 | // MVVMPlaygroundTests
4 | //
5 | // Created by Neo on 03/10/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MVVMPlayground
11 |
12 | class PhotoListViewModelTests: XCTestCase {
13 |
14 | var sut: PhotoListViewModel!
15 | var mockAPIService: MockApiService!
16 |
17 | override func setUp() {
18 | super.setUp()
19 | mockAPIService = MockApiService()
20 | sut = PhotoListViewModel(apiService: mockAPIService)
21 | }
22 |
23 | override func tearDown() {
24 | sut = nil
25 | mockAPIService = nil
26 | super.tearDown()
27 | }
28 |
29 | func test_fetch_photo() {
30 | // Given
31 | mockAPIService.completePhotos = [Photo]()
32 |
33 | // When
34 | sut.initFetch()
35 |
36 | // Assert
37 | XCTAssert(mockAPIService!.isFetchPopularPhotoCalled)
38 | }
39 |
40 | func test_fetch_photo_fail() {
41 |
42 | // Given a failed fetch with a certain failure
43 | let error = APIError.permissionDenied
44 |
45 | // When
46 | sut.initFetch()
47 |
48 | mockAPIService.fetchFail(error: error )
49 |
50 | // Sut should display predefined error message
51 | XCTAssertEqual( sut.alertMessage, error.rawValue )
52 |
53 | }
54 |
55 | func test_create_cell_view_model() {
56 | // Given
57 | let photos = StubGenerator().stubPhotos()
58 | mockAPIService.completePhotos = photos
59 | let expect = XCTestExpectation(description: "reload closure triggered")
60 | sut.reloadTableViewClosure = { () in
61 | expect.fulfill()
62 | }
63 |
64 | // When
65 | sut.initFetch()
66 | mockAPIService.fetchSuccess()
67 |
68 | // Number of cell view model is equal to the number of photos
69 | XCTAssertEqual( sut.numberOfCells, photos.count )
70 |
71 | // XCTAssert reload closure triggered
72 | wait(for: [expect], timeout: 1.0)
73 |
74 | }
75 |
76 | func test_loading_when_fetching() {
77 |
78 | //Given
79 | var loadingStatus = false
80 | let expect = XCTestExpectation(description: "Loading status updated")
81 | sut.updateLoadingStatus = { [weak sut] in
82 | loadingStatus = sut!.isLoading
83 | expect.fulfill()
84 | }
85 |
86 | //when fetching
87 | sut.initFetch()
88 |
89 | // Assert
90 | XCTAssertTrue( loadingStatus )
91 |
92 | // When finished fetching
93 | mockAPIService!.fetchSuccess()
94 | XCTAssertFalse( loadingStatus )
95 |
96 | wait(for: [expect], timeout: 1.0)
97 | }
98 |
99 | func test_user_press_for_sale_item() {
100 |
101 | //Given a sut with fetched photos
102 | let indexPath = IndexPath(row: 0, section: 0)
103 | goToFetchPhotoFinished()
104 |
105 | //When
106 | sut.userPressed( at: indexPath )
107 |
108 | //Assert
109 | XCTAssertTrue( sut.isAllowSegue )
110 | XCTAssertNotNil( sut.selectedPhoto )
111 |
112 | }
113 |
114 | func test_user_press_not_for_sale_item() {
115 |
116 | //Given a sut with fetched photos
117 | let indexPath = IndexPath(row: 4, section: 0)
118 | goToFetchPhotoFinished()
119 |
120 | let expect = XCTestExpectation(description: "Alert message is shown")
121 | sut.showAlertClosure = { [weak sut] in
122 | expect.fulfill()
123 | XCTAssertEqual(sut!.alertMessage, "This item is not for sale")
124 | }
125 |
126 | //When
127 | sut.userPressed( at: indexPath )
128 |
129 | //Assert
130 | XCTAssertFalse( sut.isAllowSegue )
131 | XCTAssertNil( sut.selectedPhoto )
132 |
133 | wait(for: [expect], timeout: 1.0)
134 | }
135 |
136 | func test_get_cell_view_model() {
137 |
138 | //Given a sut with fetched photos
139 | goToFetchPhotoFinished()
140 |
141 | let indexPath = IndexPath(row: 1, section: 0)
142 | let testPhoto = mockAPIService.completePhotos[indexPath.row]
143 |
144 | // When
145 | let vm = sut.getCellViewModel(at: indexPath)
146 |
147 | //Assert
148 | XCTAssertEqual( vm.titleText, testPhoto.name)
149 |
150 | }
151 |
152 | func test_cell_view_model() {
153 | //Given photos
154 | let today = Date()
155 | let photo = Photo(id: 1, name: "Name", description: "desc", created_at: today, image_url: "url", for_sale: true, camera: "camera")
156 | let photoWithoutCarmera = Photo(id: 1, name: "Name", description: "desc", created_at: Date(), image_url: "url", for_sale: true, camera: nil)
157 | let photoWithoutDesc = Photo(id: 1, name: "Name", description: nil, created_at: Date(), image_url: "url", for_sale: true, camera: "camera")
158 | let photoWithouCameraAndDesc = Photo(id: 1, name: "Name", description: nil, created_at: Date(), image_url: "url", for_sale: true, camera: nil)
159 |
160 | // When creat cell view model
161 | let cellViewModel = sut!.createCellViewModel( photo: photo )
162 | let cellViewModelWithoutCamera = sut!.createCellViewModel( photo: photoWithoutCarmera )
163 | let cellViewModelWithoutDesc = sut!.createCellViewModel( photo: photoWithoutDesc )
164 | let cellViewModelWithoutCameraAndDesc = sut!.createCellViewModel( photo: photoWithouCameraAndDesc )
165 |
166 | // Assert the correctness of display information
167 | XCTAssertEqual( photo.name, cellViewModel.titleText )
168 | XCTAssertEqual( photo.image_url, cellViewModel.imageUrl )
169 |
170 | XCTAssertEqual(cellViewModel.descText, "\(photo.camera!) - \(photo.description!)" )
171 | XCTAssertEqual(cellViewModelWithoutDesc.descText, photoWithoutDesc.camera! )
172 | XCTAssertEqual(cellViewModelWithoutCamera.descText, photoWithoutCarmera.description! )
173 | XCTAssertEqual(cellViewModelWithoutCameraAndDesc.descText, "" )
174 |
175 | let year = Calendar.current.component(.year, from: today)
176 | let month = Calendar.current.component(.month, from: today)
177 | let day = Calendar.current.component(.day, from: today)
178 |
179 | XCTAssertEqual( cellViewModel.dateText, String(format: "%d-%02d-%02d", year, month, day) )
180 |
181 | }
182 |
183 | }
184 |
185 | //MARK: State control
186 | extension PhotoListViewModelTests {
187 | private func goToFetchPhotoFinished() {
188 | mockAPIService.completePhotos = StubGenerator().stubPhotos()
189 | sut.initFetch()
190 | mockAPIService.fetchSuccess()
191 | }
192 | }
193 |
194 | class MockApiService: APIServiceProtocol {
195 |
196 | var isFetchPopularPhotoCalled = false
197 |
198 | var completePhotos: [Photo] = [Photo]()
199 | var completeClosure: ((Bool, [Photo], APIError?) -> ())!
200 |
201 | func fetchPopularPhoto(complete: @escaping (Bool, [Photo], APIError?) -> ()) {
202 | isFetchPopularPhotoCalled = true
203 | completeClosure = complete
204 |
205 | }
206 |
207 | func fetchSuccess() {
208 | completeClosure( true, completePhotos, nil )
209 | }
210 |
211 | func fetchFail(error: APIError?) {
212 | completeClosure( false, completePhotos, error )
213 | }
214 |
215 | }
216 |
217 | class StubGenerator {
218 | func stubPhotos() -> [Photo] {
219 | let path = Bundle.main.path(forResource: "content", ofType: "json")!
220 | let data = try! Data(contentsOf: URL(fileURLWithPath: path))
221 | let decoder = JSONDecoder()
222 | decoder.dateDecodingStrategy = .iso8601
223 | let photos = try! decoder.decode(Photos.self, from: data)
224 | return photos.photos
225 | }
226 | }
227 |
228 |
--------------------------------------------------------------------------------
/MVVMPlayground/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '11.0'
3 |
4 | target 'MVVMPlayground' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 | inhibit_all_warnings!
8 | # Pods for MVVMPlayground
9 | pod 'SDWebImage', '~> 4.0'
10 | target 'MVVMPlaygroundTests' do
11 | inherit! :search_paths
12 | # Pods for testing
13 | end
14 |
15 | end
16 |
--------------------------------------------------------------------------------
/MVVMPlayground/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - SDWebImage (4.1.0):
3 | - SDWebImage/Core (= 4.1.0)
4 | - SDWebImage/Core (4.1.0)
5 |
6 | DEPENDENCIES:
7 | - SDWebImage (~> 4.0)
8 |
9 | SPEC CHECKSUMS:
10 | SDWebImage: 0e435c14e402a6730315a0fb79f95e68654d55a4
11 |
12 | PODFILE CHECKSUM: 6172c04421e6af32559faf9d44c974ef97f08038
13 |
14 | COCOAPODS: 1.3.1
15 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - SDWebImage (4.1.0):
3 | - SDWebImage/Core (= 4.1.0)
4 | - SDWebImage/Core (4.1.0)
5 |
6 | DEPENDENCIES:
7 | - SDWebImage (~> 4.0)
8 |
9 | SPEC CHECKSUMS:
10 | SDWebImage: 0e435c14e402a6730315a0fb79f95e68654d55a4
11 |
12 | PODFILE CHECKSUM: 6172c04421e6af32559faf9d44c974ef97f08038
13 |
14 | COCOAPODS: 1.3.1
15 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009-2017 Olivier Poitrey rs@dailymotion.com
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
21 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | [](https://travis-ci.org/rs/SDWebImage)
7 | [](http://cocoadocs.org/docsets/SDWebImage/)
8 | [](http://cocoadocs.org/docsets/SDWebImage/)
9 | [](https://www.apache.org/licenses/LICENSE-2.0.html)
10 | [](https://www.versioneye.com/objective-c/sdwebimage)
11 | [](https://www.versioneye.com/objective-c/sdwebimage/references)
12 | [](https://github.com/rs/SDWebImage)
13 | [](https://codecov.io/gh/rs/SDWebImage)
14 |
15 | This library provides an async image downloader with cache support. For convenience, we added categories for UI elements like `UIImageView`, `UIButton`, `MKAnnotationView`.
16 |
17 | ## Features
18 |
19 | - [x] Categories for `UIImageView`, `UIButton`, `MKAnnotationView` adding web image and cache management
20 | - [x] An asynchronous image downloader
21 | - [x] An asynchronous memory + disk image caching with automatic cache expiration handling
22 | - [x] A background image decompression
23 | - [x] A guarantee that the same URL won't be downloaded several times
24 | - [x] A guarantee that bogus URLs won't be retried again and again
25 | - [x] A guarantee that main thread will never be blocked
26 | - [x] Performances!
27 | - [x] Use GCD and ARC
28 |
29 | ## Supported Image Formats
30 |
31 | - Image formats supported by UIImage (JPEG, PNG, ...), including GIF
32 | - WebP format, including animated WebP (use the `WebP` subspec)
33 |
34 | ## Requirements
35 |
36 | - iOS 7.0 or later
37 | - tvOS 9.0 or later
38 | - watchOS 2.0 or later
39 | - OS X 10.8 or later
40 | - Xcode 7.3 or later
41 |
42 | #### Backwards compatibility
43 |
44 | - For iOS 5 and 6, use [any 3.x version up to 3.7.6](https://github.com/rs/SDWebImage/tree/3.7.6)
45 | - For iOS < 5.0, please use the last [2.0 version](https://github.com/rs/SDWebImage/tree/2.0-compat).
46 |
47 | ## Getting Started
48 |
49 | - Read this Readme doc
50 | - Read the [How to use section](https://github.com/rs/SDWebImage#how-to-use)
51 | - Read the [documentation @ CocoaDocs](http://cocoadocs.org/docsets/SDWebImage/)
52 | - Read [How is SDWebImage better than X?](https://github.com/rs/SDWebImage/wiki/How-is-SDWebImage-better-than-X%3F)
53 | - Try the example by downloading the project from Github or even easier using CocoaPods try `pod try SDWebImage`
54 | - Get to the [installation steps](https://github.com/rs/SDWebImage#installation)
55 | - Read the [SDWebImage 4.0 Migration Guide](Docs/SDWebImage-4.0-Migration-guide.md) to get an idea of the changes from 3.x to 4.x
56 |
57 | ## Who Uses It
58 | - Find out [who uses SDWebImage](https://github.com/rs/SDWebImage/wiki/Who-Uses-SDWebImage) and add your app to the list.
59 |
60 | ## Communication
61 |
62 | - If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/sdwebimage). (Tag 'sdwebimage')
63 | - If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/sdwebimage).
64 | - If you **found a bug**, open an issue.
65 | - If you **have a feature request**, open an issue.
66 | - If you **want to contribute**, submit a pull request.
67 |
68 | ## How To Use
69 |
70 | ```objective-c
71 | Objective-C:
72 |
73 | #import
74 | ...
75 | [imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
76 | placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
77 | ```
78 |
79 | ```swift
80 | Swift:
81 |
82 | import SDWebImage
83 |
84 | imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
85 | ```
86 |
87 | - For details about how to use the library and clear examples, see [The detailed How to use](Docs/HowToUse.md)
88 |
89 | ## Animated Images (GIF) support
90 |
91 | - Starting with the 4.0 version, we rely on [FLAnimatedImage](https://github.com/Flipboard/FLAnimatedImage) to take care of our animated images.
92 | - If you use cocoapods, add `pod 'SDWebImage/GIF'` to your podfile.
93 | - To use it, simply make sure you use `FLAnimatedImageView` instead of `UIImageView`.
94 | - **Note**: there is a backwards compatible feature, so if you are still trying to load a GIF into a `UIImageView`, it will only show the 1st frame as a static image.
95 | - **Important**: FLAnimatedImage only works on the iOS platform. For OS X, use `NSImageView` with `animates` set to `YES` to show the entire animated images and `NO` to only show the 1st frame. For all the other platforms (tvOS, watchOS) we will fallback to the backwards compatibility feature described above
96 |
97 | ## Common Problems
98 |
99 | ### Using dynamic image size with UITableViewCell
100 |
101 | UITableView determines the size of the image by the first image set for a cell. If your remote images
102 | don't have the same size as your placeholder image, you may experience strange anamorphic scaling issue.
103 | The following article gives a way to workaround this issue:
104 |
105 | [http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/](http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/)
106 |
107 |
108 | ### Handle image refresh
109 |
110 | SDWebImage does very aggressive caching by default. It ignores all kind of caching control header returned by the HTTP server and cache the returned images with no time restriction. It implies your images URLs are static URLs pointing to images that never change. If the pointed image happen to change, some parts of the URL should change accordingly.
111 |
112 | If you don't control the image server you're using, you may not be able to change the URL when its content is updated. This is the case for Facebook avatar URLs for instance. In such case, you may use the `SDWebImageRefreshCached` flag. This will slightly degrade the performance but will respect the HTTP caching control headers:
113 |
114 | ``` objective-c
115 | [imageView sd_setImageWithURL:[NSURL URLWithString:@"https://graph.facebook.com/olivier.poitrey/picture"]
116 | placeholderImage:[UIImage imageNamed:@"avatar-placeholder.png"]
117 | options:SDWebImageRefreshCached];
118 | ```
119 |
120 | ### Add a progress indicator
121 |
122 | Add these before you call ```sd_setImageWithURL```
123 |
124 | ``` objective-c
125 | [imageView sd_setShowActivityIndicatorView:YES];
126 | [imageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray];
127 | ```
128 |
129 | ``` swift
130 | imageView.sd_setShowActivityIndicatorView(true)
131 | imageView.sd_setIndicatorStyle(.Gray)
132 | ```
133 |
134 | ## Installation
135 |
136 | There are three ways to use SDWebImage in your project:
137 | - using CocoaPods
138 | - using Carthage
139 | - by cloning the project into your repository
140 |
141 | ### Installation with CocoaPods
142 |
143 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries in your projects. See the [Get Started](http://cocoapods.org/#get_started) section for more details.
144 |
145 | #### Podfile
146 | ```
147 | platform :ios, '7.0'
148 | pod 'SDWebImage', '~> 4.0'
149 | ```
150 |
151 | If you are using Swift, be sure to add `use_frameworks!` and set your target to iOS 8+:
152 | ```
153 | platform :ios, '8.0'
154 | use_frameworks!
155 | ```
156 |
157 | #### Subspecs
158 |
159 | There are 4 subspecs available now: `Core`, `MapKit`, `GIF` and `WebP` (this means you can install only some of the SDWebImage modules. By default, you get just `Core`, so if you need `WebP`, you need to specify it).
160 |
161 | Podfile example:
162 | ```
163 | pod 'SDWebImage/WebP'
164 | ```
165 |
166 | ### Installation with Carthage (iOS 8+)
167 |
168 | [Carthage](https://github.com/Carthage/Carthage) is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods.
169 |
170 | To install with carthage, follow the instruction on [Carthage](https://github.com/Carthage/Carthage)
171 |
172 | #### Cartfile
173 | ```
174 | github "rs/SDWebImage"
175 | ```
176 |
177 | ### Installation by cloning the repository
178 | - see [Manual install](Docs/ManualInstallation.md)
179 |
180 | ### Import headers in your source files
181 |
182 | In the source files where you need to use the library, import the header file:
183 |
184 | ```objective-c
185 | #import
186 | ```
187 |
188 | ### Build Project
189 |
190 | At this point your workspace should build without error. If you are having problem, post to the Issue and the
191 | community can help you solve it.
192 |
193 | ## Author
194 | - [Olivier Poitrey](https://github.com/rs)
195 |
196 | ## Collaborators
197 | - [Konstantinos K.](https://github.com/mythodeia)
198 | - [Bogdan Poplauschi](https://github.com/bpoplauschi)
199 | - [Chester Liu](https://github.com/skyline75489)
200 |
201 | ## Licenses
202 |
203 | All source code is licensed under the [MIT License](https://raw.github.com/rs/SDWebImage/master/LICENSE).
204 |
205 | ## Architecture
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Fabrice Aneche
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 | #import "SDWebImageCompat.h"
12 |
13 | typedef NS_ENUM(NSInteger, SDImageFormat) {
14 | SDImageFormatUndefined = -1,
15 | SDImageFormatJPEG = 0,
16 | SDImageFormatPNG,
17 | SDImageFormatGIF,
18 | SDImageFormatTIFF,
19 | SDImageFormatWebP
20 | };
21 |
22 | @interface NSData (ImageContentType)
23 |
24 | /**
25 | * Return image format
26 | *
27 | * @param data the input image data
28 | *
29 | * @return the image format as `SDImageFormat` (enum)
30 | */
31 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/NSData+ImageContentType.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Fabrice Aneche
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "NSData+ImageContentType.h"
11 |
12 |
13 | @implementation NSData (ImageContentType)
14 |
15 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
16 | if (!data) {
17 | return SDImageFormatUndefined;
18 | }
19 |
20 | uint8_t c;
21 | [data getBytes:&c length:1];
22 | switch (c) {
23 | case 0xFF:
24 | return SDImageFormatJPEG;
25 | case 0x89:
26 | return SDImageFormatPNG;
27 | case 0x47:
28 | return SDImageFormatGIF;
29 | case 0x49:
30 | case 0x4D:
31 | return SDImageFormatTIFF;
32 | case 0x52:
33 | // R as RIFF for WEBP
34 | if (data.length < 12) {
35 | return SDImageFormatUndefined;
36 | }
37 |
38 | NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
39 | if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
40 | return SDImageFormatWebP;
41 | }
42 | }
43 | return SDImageFormatUndefined;
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/NSImage+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_MAC
12 |
13 | #import
14 |
15 | @interface NSImage (WebCache)
16 |
17 | - (CGImageRef)CGImage;
18 | - (NSArray *)images;
19 | - (BOOL)isGIF;
20 |
21 | @end
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/NSImage+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "NSImage+WebCache.h"
10 |
11 | #if SD_MAC
12 |
13 | @implementation NSImage (WebCache)
14 |
15 | - (CGImageRef)CGImage {
16 | NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
17 | CGImageRef cgImage = [self CGImageForProposedRect:&imageRect context:NULL hints:nil];
18 | return cgImage;
19 | }
20 |
21 | - (NSArray *)images {
22 | return nil;
23 | }
24 |
25 | - (BOOL)isGIF {
26 | return NO;
27 | }
28 |
29 | @end
30 |
31 | #endif
32 |
33 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDImageCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 | #import "SDImageCacheConfig.h"
12 |
13 | typedef NS_ENUM(NSInteger, SDImageCacheType) {
14 | /**
15 | * The image wasn't available the SDWebImage caches, but was downloaded from the web.
16 | */
17 | SDImageCacheTypeNone,
18 | /**
19 | * The image was obtained from the disk cache.
20 | */
21 | SDImageCacheTypeDisk,
22 | /**
23 | * The image was obtained from the memory cache.
24 | */
25 | SDImageCacheTypeMemory
26 | };
27 |
28 | typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);
29 |
30 | typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
31 |
32 | typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
33 |
34 |
35 | /**
36 | * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
37 | * asynchronous so it doesn’t add unnecessary latency to the UI.
38 | */
39 | @interface SDImageCache : NSObject
40 |
41 | #pragma mark - Properties
42 |
43 | /**
44 | * Cache Config object - storing all kind of settings
45 | */
46 | @property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
47 |
48 | /**
49 | * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
50 | */
51 | @property (assign, nonatomic) NSUInteger maxMemoryCost;
52 |
53 | /**
54 | * The maximum number of objects the cache should hold.
55 | */
56 | @property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
57 |
58 | #pragma mark - Singleton and initialization
59 |
60 | /**
61 | * Returns global shared cache instance
62 | *
63 | * @return SDImageCache global instance
64 | */
65 | + (nonnull instancetype)sharedImageCache;
66 |
67 | /**
68 | * Init a new cache store with a specific namespace
69 | *
70 | * @param ns The namespace to use for this cache store
71 | */
72 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;
73 |
74 | /**
75 | * Init a new cache store with a specific namespace and directory
76 | *
77 | * @param ns The namespace to use for this cache store
78 | * @param directory Directory to cache disk images in
79 | */
80 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
81 | diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
82 |
83 | #pragma mark - Cache paths
84 |
85 | - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
86 |
87 | /**
88 | * Add a read-only cache path to search for images pre-cached by SDImageCache
89 | * Useful if you want to bundle pre-loaded images with your app
90 | *
91 | * @param path The path to use for this read-only cache path
92 | */
93 | - (void)addReadOnlyCachePath:(nonnull NSString *)path;
94 |
95 | #pragma mark - Store Ops
96 |
97 | /**
98 | * Asynchronously store an image into memory and disk cache at the given key.
99 | *
100 | * @param image The image to store
101 | * @param key The unique image cache key, usually it's image absolute URL
102 | * @param completionBlock A block executed after the operation is finished
103 | */
104 | - (void)storeImage:(nullable UIImage *)image
105 | forKey:(nullable NSString *)key
106 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
107 |
108 | /**
109 | * Asynchronously store an image into memory and disk cache at the given key.
110 | *
111 | * @param image The image to store
112 | * @param key The unique image cache key, usually it's image absolute URL
113 | * @param toDisk Store the image to disk cache if YES
114 | * @param completionBlock A block executed after the operation is finished
115 | */
116 | - (void)storeImage:(nullable UIImage *)image
117 | forKey:(nullable NSString *)key
118 | toDisk:(BOOL)toDisk
119 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
120 |
121 | /**
122 | * Asynchronously store an image into memory and disk cache at the given key.
123 | *
124 | * @param image The image to store
125 | * @param imageData The image data as returned by the server, this representation will be used for disk storage
126 | * instead of converting the given image object into a storable/compressed image format in order
127 | * to save quality and CPU
128 | * @param key The unique image cache key, usually it's image absolute URL
129 | * @param toDisk Store the image to disk cache if YES
130 | * @param completionBlock A block executed after the operation is finished
131 | */
132 | - (void)storeImage:(nullable UIImage *)image
133 | imageData:(nullable NSData *)imageData
134 | forKey:(nullable NSString *)key
135 | toDisk:(BOOL)toDisk
136 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
137 |
138 | /**
139 | * Synchronously store image NSData into disk cache at the given key.
140 | *
141 | * @warning This method is synchronous, make sure to call it from the ioQueue
142 | *
143 | * @param imageData The image data to store
144 | * @param key The unique image cache key, usually it's image absolute URL
145 | */
146 | - (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
147 |
148 | #pragma mark - Query and Retrieve Ops
149 |
150 | /**
151 | * Async check if image exists in disk cache already (does not load the image)
152 | *
153 | * @param key the key describing the url
154 | * @param completionBlock the block to be executed when the check is done.
155 | * @note the completion block will be always executed on the main queue
156 | */
157 | - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
158 |
159 | /**
160 | * Operation that queries the cache asynchronously and call the completion when done.
161 | *
162 | * @param key The unique key used to store the wanted image
163 | * @param doneBlock The completion block. Will not get called if the operation is cancelled
164 | *
165 | * @return a NSOperation instance containing the cache op
166 | */
167 | - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
168 |
169 | /**
170 | * Query the memory cache synchronously.
171 | *
172 | * @param key The unique key used to store the image
173 | */
174 | - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
175 |
176 | /**
177 | * Query the disk cache synchronously.
178 | *
179 | * @param key The unique key used to store the image
180 | */
181 | - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
182 |
183 | /**
184 | * Query the cache (memory and or disk) synchronously after checking the memory cache.
185 | *
186 | * @param key The unique key used to store the image
187 | */
188 | - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
189 |
190 | #pragma mark - Remove Ops
191 |
192 | /**
193 | * Remove the image from memory and disk cache asynchronously
194 | *
195 | * @param key The unique image cache key
196 | * @param completion A block that should be executed after the image has been removed (optional)
197 | */
198 | - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
199 |
200 | /**
201 | * Remove the image from memory and optionally disk cache asynchronously
202 | *
203 | * @param key The unique image cache key
204 | * @param fromDisk Also remove cache entry from disk if YES
205 | * @param completion A block that should be executed after the image has been removed (optional)
206 | */
207 | - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
208 |
209 | #pragma mark - Cache clean Ops
210 |
211 | /**
212 | * Clear all memory cached images
213 | */
214 | - (void)clearMemory;
215 |
216 | /**
217 | * Async clear all disk cached images. Non-blocking method - returns immediately.
218 | * @param completion A block that should be executed after cache expiration completes (optional)
219 | */
220 | - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
221 |
222 | /**
223 | * Async remove all expired cached image from disk. Non-blocking method - returns immediately.
224 | * @param completionBlock A block that should be executed after cache expiration completes (optional)
225 | */
226 | - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
227 |
228 | #pragma mark - Cache Info
229 |
230 | /**
231 | * Get the size used by the disk cache
232 | */
233 | - (NSUInteger)getSize;
234 |
235 | /**
236 | * Get the number of images in the disk cache
237 | */
238 | - (NSUInteger)getDiskCount;
239 |
240 | /**
241 | * Asynchronously calculate the disk cache's size.
242 | */
243 | - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
244 |
245 | #pragma mark - Cache Paths
246 |
247 | /**
248 | * Get the cache path for a certain key (needs the cache path root folder)
249 | *
250 | * @param key the key (can be obtained from url using cacheKeyForURL)
251 | * @param path the cache path root folder
252 | *
253 | * @return the cache path
254 | */
255 | - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
256 |
257 | /**
258 | * Get the default cache path for a certain key
259 | *
260 | * @param key the key (can be obtained from url using cacheKeyForURL)
261 | *
262 | * @return the default cache path
263 | */
264 | - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;
265 |
266 | @end
267 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDImageCacheConfig.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 |
12 | @interface SDImageCacheConfig : NSObject
13 |
14 | /**
15 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
16 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
17 | */
18 | @property (assign, nonatomic) BOOL shouldDecompressImages;
19 |
20 | /**
21 | * disable iCloud backup [defaults to YES]
22 | */
23 | @property (assign, nonatomic) BOOL shouldDisableiCloud;
24 |
25 | /**
26 | * use memory cache [defaults to YES]
27 | */
28 | @property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
29 |
30 | /**
31 | * The maximum length of time to keep an image in the cache, in seconds
32 | */
33 | @property (assign, nonatomic) NSInteger maxCacheAge;
34 |
35 | /**
36 | * The maximum size of the cache, in bytes.
37 | */
38 | @property (assign, nonatomic) NSUInteger maxCacheSize;
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDImageCacheConfig.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDImageCacheConfig.h"
10 |
11 | static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
12 |
13 | @implementation SDImageCacheConfig
14 |
15 | - (instancetype)init {
16 | if (self = [super init]) {
17 | _shouldDecompressImages = YES;
18 | _shouldDisableiCloud = YES;
19 | _shouldCacheImagesInMemory = YES;
20 | _maxCacheAge = kDefaultCacheMaxCacheAge;
21 | _maxCacheSize = 0;
22 | }
23 | return self;
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImageCompat.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Jamie Pinkham
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 |
12 | #ifdef __OBJC_GC__
13 | #error SDWebImage does not support Objective-C Garbage Collection
14 | #endif
15 |
16 | // Apple's defines from TargetConditionals.h are a bit weird.
17 | // Seems like TARGET_OS_MAC is always defined (on all platforms).
18 | // To determine if we are running on OSX, we can only rely on TARGET_OS_IPHONE=0 and all the other platforms
19 | #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
20 | #define SD_MAC 1
21 | #else
22 | #define SD_MAC 0
23 | #endif
24 |
25 | // iOS and tvOS are very similar, UIKit exists on both platforms
26 | // Note: watchOS also has UIKit, but it's very limited
27 | #if TARGET_OS_IOS || TARGET_OS_TV
28 | #define SD_UIKIT 1
29 | #else
30 | #define SD_UIKIT 0
31 | #endif
32 |
33 | #if TARGET_OS_IOS
34 | #define SD_IOS 1
35 | #else
36 | #define SD_IOS 0
37 | #endif
38 |
39 | #if TARGET_OS_TV
40 | #define SD_TV 1
41 | #else
42 | #define SD_TV 0
43 | #endif
44 |
45 | #if TARGET_OS_WATCH
46 | #define SD_WATCH 1
47 | #else
48 | #define SD_WATCH 0
49 | #endif
50 |
51 |
52 | #if SD_MAC
53 | #import
54 | #ifndef UIImage
55 | #define UIImage NSImage
56 | #endif
57 | #ifndef UIImageView
58 | #define UIImageView NSImageView
59 | #endif
60 | #ifndef UIView
61 | #define UIView NSView
62 | #endif
63 | #else
64 | #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
65 | #error SDWebImage doesn't support Deployment Target version < 5.0
66 | #endif
67 |
68 | #if SD_UIKIT
69 | #import
70 | #endif
71 | #if SD_WATCH
72 | #import
73 | #endif
74 | #endif
75 |
76 | #ifndef NS_ENUM
77 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
78 | #endif
79 |
80 | #ifndef NS_OPTIONS
81 | #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
82 | #endif
83 |
84 | #if OS_OBJECT_USE_OBJC
85 | #undef SDDispatchQueueRelease
86 | #undef SDDispatchQueueSetterSementics
87 | #define SDDispatchQueueRelease(q)
88 | #define SDDispatchQueueSetterSementics strong
89 | #else
90 | #undef SDDispatchQueueRelease
91 | #undef SDDispatchQueueSetterSementics
92 | #define SDDispatchQueueRelease(q) (dispatch_release(q))
93 | #define SDDispatchQueueSetterSementics assign
94 | #endif
95 |
96 | extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
97 |
98 | typedef void(^SDWebImageNoParamsBlock)();
99 |
100 | extern NSString *const SDWebImageErrorDomain;
101 |
102 | #ifndef dispatch_main_async_safe
103 | #define dispatch_main_async_safe(block)\
104 | if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
105 | block();\
106 | } else {\
107 | dispatch_async(dispatch_get_main_queue(), block);\
108 | }
109 | #endif
110 |
111 | static int64_t kAsyncTestTimeout = 5;
112 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImageCompat.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #import "objc/runtime.h"
12 |
13 | #if !__has_feature(objc_arc)
14 | #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
15 | #endif
16 |
17 | inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
18 | if (!image) {
19 | return nil;
20 | }
21 |
22 | #if SD_MAC
23 | return image;
24 | #elif SD_UIKIT || SD_WATCH
25 | if ((image.images).count > 0) {
26 | NSMutableArray *scaledImages = [NSMutableArray array];
27 |
28 | for (UIImage *tempImage in image.images) {
29 | [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
30 | }
31 |
32 | UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];
33 | #ifdef SD_WEBP
34 | if (animatedImage) {
35 | SEL sd_webpLoopCount = NSSelectorFromString(@"sd_webpLoopCount");
36 | NSNumber *value = objc_getAssociatedObject(image, sd_webpLoopCount);
37 | NSInteger loopCount = value.integerValue;
38 | if (loopCount) {
39 | objc_setAssociatedObject(animatedImage, sd_webpLoopCount, @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
40 | }
41 | }
42 | #endif
43 | return animatedImage;
44 | } else {
45 | #if SD_WATCH
46 | if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
47 | #elif SD_UIKIT
48 | if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
49 | #endif
50 | CGFloat scale = 1;
51 | if (key.length >= 8) {
52 | NSRange range = [key rangeOfString:@"@2x."];
53 | if (range.location != NSNotFound) {
54 | scale = 2.0;
55 | }
56 |
57 | range = [key rangeOfString:@"@3x."];
58 | if (range.location != NSNotFound) {
59 | scale = 3.0;
60 | }
61 | }
62 |
63 | UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
64 | image = scaledImage;
65 | }
66 | return image;
67 | }
68 | #endif
69 | }
70 |
71 | NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";
72 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImageDecoder.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) james
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 | #import "SDWebImageCompat.h"
12 |
13 | @interface UIImage (ForceDecode)
14 |
15 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image;
16 |
17 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImageDownloaderOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageDownloader.h"
11 | #import "SDWebImageOperation.h"
12 |
13 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
14 | extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
15 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
16 | extern NSString * _Nonnull const SDWebImageDownloadFinishNotification;
17 |
18 |
19 |
20 | /**
21 | Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol
22 | */
23 | @protocol SDWebImageDownloaderOperationInterface
24 |
25 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
26 | inSession:(nullable NSURLSession *)session
27 | options:(SDWebImageDownloaderOptions)options;
28 |
29 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
30 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
31 |
32 | - (BOOL)shouldDecompressImages;
33 | - (void)setShouldDecompressImages:(BOOL)value;
34 |
35 | - (nullable NSURLCredential *)credential;
36 | - (void)setCredential:(nullable NSURLCredential *)value;
37 |
38 | @end
39 |
40 |
41 | @interface SDWebImageDownloaderOperation : NSOperation
42 |
43 | /**
44 | * The request used by the operation's task.
45 | */
46 | @property (strong, nonatomic, readonly, nullable) NSURLRequest *request;
47 |
48 | /**
49 | * The operation's task
50 | */
51 | @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
52 |
53 |
54 | @property (assign, nonatomic) BOOL shouldDecompressImages;
55 |
56 | /**
57 | * Was used to determine whether the URL connection should consult the credential storage for authenticating the connection.
58 | * @deprecated Not used for a couple of versions
59 | */
60 | @property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility");
61 |
62 | /**
63 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
64 | *
65 | * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
66 | */
67 | @property (nonatomic, strong, nullable) NSURLCredential *credential;
68 |
69 | /**
70 | * The SDWebImageDownloaderOptions for the receiver.
71 | */
72 | @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
73 |
74 | /**
75 | * The expected size of data.
76 | */
77 | @property (assign, nonatomic) NSInteger expectedSize;
78 |
79 | /**
80 | * The response returned by the operation's connection.
81 | */
82 | @property (strong, nonatomic, nullable) NSURLResponse *response;
83 |
84 | /**
85 | * Initializes a `SDWebImageDownloaderOperation` object
86 | *
87 | * @see SDWebImageDownloaderOperation
88 | *
89 | * @param request the URL request
90 | * @param session the URL session in which this operation will run
91 | * @param options downloader options
92 | *
93 | * @return the initialized instance
94 | */
95 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
96 | inSession:(nullable NSURLSession *)session
97 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;
98 |
99 | /**
100 | * Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of
101 | * callbacks.
102 | *
103 | * @param progressBlock the block executed when a new chunk of data arrives.
104 | * @note the progress block is executed on a background queue
105 | * @param completedBlock the block executed when the download is done.
106 | * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
107 | *
108 | * @return the token to use to cancel this set of handlers
109 | */
110 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
111 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
112 |
113 | /**
114 | * Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled.
115 | *
116 | * @param token the token representing a set of callbacks to cancel
117 | *
118 | * @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise.
119 | */
120 | - (BOOL)cancel:(nullable id)token;
121 |
122 | @end
123 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImageOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 |
11 | @protocol SDWebImageOperation
12 |
13 | - (void)cancel;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageManager.h"
11 |
12 | @class SDWebImagePrefetcher;
13 |
14 | @protocol SDWebImagePrefetcherDelegate
15 |
16 | @optional
17 |
18 | /**
19 | * Called when an image was prefetched.
20 | *
21 | * @param imagePrefetcher The current image prefetcher
22 | * @param imageURL The image url that was prefetched
23 | * @param finishedCount The total number of images that were prefetched (successful or not)
24 | * @param totalCount The total number of images that were to be prefetched
25 | */
26 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
27 |
28 | /**
29 | * Called when all images are prefetched.
30 | * @param imagePrefetcher The current image prefetcher
31 | * @param totalCount The total number of images that were prefetched (whether successful or not)
32 | * @param skippedCount The total number of images that were skipped
33 | */
34 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
35 |
36 | @end
37 |
38 | typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
39 | typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
40 |
41 | /**
42 | * Prefetch some URLs in the cache for future use. Images are downloaded in low priority.
43 | */
44 | @interface SDWebImagePrefetcher : NSObject
45 |
46 | /**
47 | * The web image manager
48 | */
49 | @property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;
50 |
51 | /**
52 | * Maximum number of URLs to prefetch at the same time. Defaults to 3.
53 | */
54 | @property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
55 |
56 | /**
57 | * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority.
58 | */
59 | @property (nonatomic, assign) SDWebImageOptions options;
60 |
61 | /**
62 | * Queue options for Prefetcher. Defaults to Main Queue.
63 | */
64 | @property (SDDispatchQueueSetterSementics, nonatomic, nonnull) dispatch_queue_t prefetcherQueue;
65 |
66 | @property (weak, nonatomic, nullable) id delegate;
67 |
68 | /**
69 | * Return the global image prefetcher instance.
70 | */
71 | + (nonnull instancetype)sharedImagePrefetcher;
72 |
73 | /**
74 | * Allows you to instantiate a prefetcher with any arbitrary image manager.
75 | */
76 | - (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
77 |
78 | /**
79 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
80 | * currently one image is downloaded at a time,
81 | * and skips images for failed downloads and proceed to the next image in the list.
82 | * Any previously-running prefetch operations are canceled.
83 | *
84 | * @param urls list of URLs to prefetch
85 | */
86 | - (void)prefetchURLs:(nullable NSArray *)urls;
87 |
88 | /**
89 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
90 | * currently one image is downloaded at a time,
91 | * and skips images for failed downloads and proceed to the next image in the list.
92 | * Any previously-running prefetch operations are canceled.
93 | *
94 | * @param urls list of URLs to prefetch
95 | * @param progressBlock block to be called when progress updates;
96 | * first parameter is the number of completed (successful or not) requests,
97 | * second parameter is the total number of images originally requested to be prefetched
98 | * @param completionBlock block to be called when prefetching is completed
99 | * first param is the number of completed (successful or not) requests,
100 | * second parameter is the number of skipped requests
101 | */
102 | - (void)prefetchURLs:(nullable NSArray *)urls
103 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
104 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
105 |
106 | /**
107 | * Remove and cancel queued list
108 | */
109 | - (void)cancelPrefetching;
110 |
111 |
112 | @end
113 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/SDWebImagePrefetcher.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImagePrefetcher.h"
10 |
11 | @interface SDWebImagePrefetcher ()
12 |
13 | @property (strong, nonatomic, nonnull) SDWebImageManager *manager;
14 | @property (strong, nonatomic, nullable) NSArray *prefetchURLs;
15 | @property (assign, nonatomic) NSUInteger requestedCount;
16 | @property (assign, nonatomic) NSUInteger skippedCount;
17 | @property (assign, nonatomic) NSUInteger finishedCount;
18 | @property (assign, nonatomic) NSTimeInterval startedTime;
19 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
20 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
21 |
22 | @end
23 |
24 | @implementation SDWebImagePrefetcher
25 |
26 | + (nonnull instancetype)sharedImagePrefetcher {
27 | static dispatch_once_t once;
28 | static id instance;
29 | dispatch_once(&once, ^{
30 | instance = [self new];
31 | });
32 | return instance;
33 | }
34 |
35 | - (nonnull instancetype)init {
36 | return [self initWithImageManager:[SDWebImageManager new]];
37 | }
38 |
39 | - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
40 | if ((self = [super init])) {
41 | _manager = manager;
42 | _options = SDWebImageLowPriority;
43 | _prefetcherQueue = dispatch_get_main_queue();
44 | self.maxConcurrentDownloads = 3;
45 | }
46 | return self;
47 | }
48 |
49 | - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
50 | self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
51 | }
52 |
53 | - (NSUInteger)maxConcurrentDownloads {
54 | return self.manager.imageDownloader.maxConcurrentDownloads;
55 | }
56 |
57 | - (void)startPrefetchingAtIndex:(NSUInteger)index {
58 | if (index >= self.prefetchURLs.count) return;
59 | self.requestedCount++;
60 | [self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
61 | if (!finished) return;
62 | self.finishedCount++;
63 |
64 | if (image) {
65 | if (self.progressBlock) {
66 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
67 | }
68 | }
69 | else {
70 | if (self.progressBlock) {
71 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
72 | }
73 | // Add last failed
74 | self.skippedCount++;
75 | }
76 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
77 | [self.delegate imagePrefetcher:self
78 | didPrefetchURL:self.prefetchURLs[index]
79 | finishedCount:self.finishedCount
80 | totalCount:self.prefetchURLs.count
81 | ];
82 | }
83 | if (self.prefetchURLs.count > self.requestedCount) {
84 | dispatch_async(self.prefetcherQueue, ^{
85 | [self startPrefetchingAtIndex:self.requestedCount];
86 | });
87 | } else if (self.finishedCount == self.requestedCount) {
88 | [self reportStatus];
89 | if (self.completionBlock) {
90 | self.completionBlock(self.finishedCount, self.skippedCount);
91 | self.completionBlock = nil;
92 | }
93 | self.progressBlock = nil;
94 | }
95 | }];
96 | }
97 |
98 | - (void)reportStatus {
99 | NSUInteger total = (self.prefetchURLs).count;
100 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
101 | [self.delegate imagePrefetcher:self
102 | didFinishWithTotalCount:(total - self.skippedCount)
103 | skippedCount:self.skippedCount
104 | ];
105 | }
106 | }
107 |
108 | - (void)prefetchURLs:(nullable NSArray *)urls {
109 | [self prefetchURLs:urls progress:nil completed:nil];
110 | }
111 |
112 | - (void)prefetchURLs:(nullable NSArray *)urls
113 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
114 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
115 | [self cancelPrefetching]; // Prevent duplicate prefetch request
116 | self.startedTime = CFAbsoluteTimeGetCurrent();
117 | self.prefetchURLs = urls;
118 | self.completionBlock = completionBlock;
119 | self.progressBlock = progressBlock;
120 |
121 | if (urls.count == 0) {
122 | if (completionBlock) {
123 | completionBlock(0,0);
124 | }
125 | } else {
126 | // Starts prefetching from the very first image on the list with the max allowed concurrency
127 | NSUInteger listCount = self.prefetchURLs.count;
128 | for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
129 | [self startPrefetchingAtIndex:i];
130 | }
131 | }
132 | }
133 |
134 | - (void)cancelPrefetching {
135 | self.prefetchURLs = nil;
136 | self.skippedCount = 0;
137 | self.requestedCount = 0;
138 | self.finishedCount = 0;
139 | [self.manager cancelAll];
140 | }
141 |
142 | @end
143 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIButton+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIButton+WebCache.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 | #import "UIView+WebCache.h"
16 |
17 | static char imageURLStorageKey;
18 |
19 | typedef NSMutableDictionary SDStateImageURLDictionary;
20 |
21 | @implementation UIButton (WebCache)
22 |
23 | - (nullable NSURL *)sd_currentImageURL {
24 | NSURL *url = self.imageURLStorage[@(self.state)];
25 |
26 | if (!url) {
27 | url = self.imageURLStorage[@(UIControlStateNormal)];
28 | }
29 |
30 | return url;
31 | }
32 |
33 | - (nullable NSURL *)sd_imageURLForState:(UIControlState)state {
34 | return self.imageURLStorage[@(state)];
35 | }
36 |
37 | #pragma mark - Image
38 |
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
40 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
41 | }
42 |
43 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
44 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
45 | }
46 |
47 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
48 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
49 | }
50 |
51 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
52 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
53 | }
54 |
55 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
56 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
57 | }
58 |
59 | - (void)sd_setImageWithURL:(nullable NSURL *)url
60 | forState:(UIControlState)state
61 | placeholderImage:(nullable UIImage *)placeholder
62 | options:(SDWebImageOptions)options
63 | completed:(nullable SDExternalCompletionBlock)completedBlock {
64 | if (!url) {
65 | [self.imageURLStorage removeObjectForKey:@(state)];
66 | return;
67 | }
68 |
69 | self.imageURLStorage[@(state)] = url;
70 |
71 | __weak typeof(self)weakSelf = self;
72 | [self sd_internalSetImageWithURL:url
73 | placeholderImage:placeholder
74 | options:options
75 | operationKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]
76 | setImageBlock:^(UIImage *image, NSData *imageData) {
77 | [weakSelf setImage:image forState:state];
78 | }
79 | progress:nil
80 | completed:completedBlock];
81 | }
82 |
83 | #pragma mark - Background image
84 |
85 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
86 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
87 | }
88 |
89 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
90 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
91 | }
92 |
93 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
94 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
95 | }
96 |
97 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
98 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
99 | }
100 |
101 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
102 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
103 | }
104 |
105 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
106 | forState:(UIControlState)state
107 | placeholderImage:(nullable UIImage *)placeholder
108 | options:(SDWebImageOptions)options
109 | completed:(nullable SDExternalCompletionBlock)completedBlock {
110 | if (!url) {
111 | [self.imageURLStorage removeObjectForKey:@(state)];
112 | return;
113 | }
114 |
115 | self.imageURLStorage[@(state)] = url;
116 |
117 | __weak typeof(self)weakSelf = self;
118 | [self sd_internalSetImageWithURL:url
119 | placeholderImage:placeholder
120 | options:options
121 | operationKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]
122 | setImageBlock:^(UIImage *image, NSData *imageData) {
123 | [weakSelf setBackgroundImage:image forState:state];
124 | }
125 | progress:nil
126 | completed:completedBlock];
127 | }
128 |
129 | - (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state {
130 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
131 | }
132 |
133 | - (void)sd_cancelImageLoadForState:(UIControlState)state {
134 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
135 | }
136 |
137 | - (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state {
138 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
139 | }
140 |
141 | - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state {
142 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
143 | }
144 |
145 | - (SDStateImageURLDictionary *)imageURLStorage {
146 | SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
147 | if (!storage) {
148 | storage = [NSMutableDictionary dictionary];
149 | objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
150 | }
151 |
152 | return storage;
153 | }
154 |
155 | @end
156 |
157 | #endif
158 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImage+GIF.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Laurin Brandner
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "SDWebImageCompat.h"
11 |
12 | @interface UIImage (GIF)
13 |
14 | /**
15 | * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image
16 | */
17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data;
18 |
19 | /**
20 | * Checks if an UIImage instance is a GIF. Will use the `images` array
21 | */
22 | - (BOOL)isGIF;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImage+GIF.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Laurin Brandner
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "UIImage+GIF.h"
11 | #import
12 | #import "objc/runtime.h"
13 | #import "NSImage+WebCache.h"
14 |
15 | @implementation UIImage (GIF)
16 |
17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data {
18 | if (!data) {
19 | return nil;
20 | }
21 |
22 | #if SD_MAC
23 | return [[UIImage alloc] initWithData:data];
24 | #endif
25 |
26 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
27 |
28 | size_t count = CGImageSourceGetCount(source);
29 |
30 | UIImage *staticImage;
31 |
32 | if (count <= 1) {
33 | staticImage = [[UIImage alloc] initWithData:data];
34 | } else {
35 | // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
36 | // this here is only code to allow drawing animated images as static ones
37 | #if SD_WATCH
38 | CGFloat scale = 1;
39 | scale = [WKInterfaceDevice currentDevice].screenScale;
40 | #elif SD_UIKIT
41 | CGFloat scale = 1;
42 | scale = [UIScreen mainScreen].scale;
43 | #endif
44 |
45 | CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
46 | #if SD_UIKIT || SD_WATCH
47 | UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
48 | staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
49 | #endif
50 | CGImageRelease(CGImage);
51 | }
52 |
53 | CFRelease(source);
54 |
55 | return staticImage;
56 | }
57 |
58 | - (BOOL)isGIF {
59 | return (self.images != nil);
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 | #import "NSData+ImageContentType.h"
11 |
12 | @interface UIImage (MultiFormat)
13 |
14 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data;
15 | - (nullable NSData *)sd_imageData;
16 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImage+MultiFormat.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImage+MultiFormat.h"
10 | #import "UIImage+GIF.h"
11 | #import "NSData+ImageContentType.h"
12 | #import
13 |
14 | #ifdef SD_WEBP
15 | #import "UIImage+WebP.h"
16 | #endif
17 |
18 | @implementation UIImage (MultiFormat)
19 |
20 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data {
21 | if (!data) {
22 | return nil;
23 | }
24 |
25 | UIImage *image;
26 | SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
27 | if (imageFormat == SDImageFormatGIF) {
28 | image = [UIImage sd_animatedGIFWithData:data];
29 | }
30 | #ifdef SD_WEBP
31 | else if (imageFormat == SDImageFormatWebP)
32 | {
33 | image = [UIImage sd_imageWithWebPData:data];
34 | }
35 | #endif
36 | else {
37 | image = [[UIImage alloc] initWithData:data];
38 | #if SD_UIKIT || SD_WATCH
39 | UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
40 | if (orientation != UIImageOrientationUp) {
41 | image = [UIImage imageWithCGImage:image.CGImage
42 | scale:image.scale
43 | orientation:orientation];
44 | }
45 | #endif
46 | }
47 |
48 |
49 | return image;
50 | }
51 |
52 | #if SD_UIKIT || SD_WATCH
53 | +(UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData {
54 | UIImageOrientation result = UIImageOrientationUp;
55 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
56 | if (imageSource) {
57 | CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
58 | if (properties) {
59 | CFTypeRef val;
60 | int exifOrientation;
61 | val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
62 | if (val) {
63 | CFNumberGetValue(val, kCFNumberIntType, &exifOrientation);
64 | result = [self sd_exifOrientationToiOSOrientation:exifOrientation];
65 | } // else - if it's not set it remains at up
66 | CFRelease((CFTypeRef) properties);
67 | } else {
68 | //NSLog(@"NO PROPERTIES, FAIL");
69 | }
70 | CFRelease(imageSource);
71 | }
72 | return result;
73 | }
74 |
75 | #pragma mark EXIF orientation tag converter
76 | // Convert an EXIF image orientation to an iOS one.
77 | // reference see here: http://sylvana.net/jpegcrop/exif_orientation.html
78 | + (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation {
79 | UIImageOrientation orientation = UIImageOrientationUp;
80 | switch (exifOrientation) {
81 | case 1:
82 | orientation = UIImageOrientationUp;
83 | break;
84 |
85 | case 3:
86 | orientation = UIImageOrientationDown;
87 | break;
88 |
89 | case 8:
90 | orientation = UIImageOrientationLeft;
91 | break;
92 |
93 | case 6:
94 | orientation = UIImageOrientationRight;
95 | break;
96 |
97 | case 2:
98 | orientation = UIImageOrientationUpMirrored;
99 | break;
100 |
101 | case 4:
102 | orientation = UIImageOrientationDownMirrored;
103 | break;
104 |
105 | case 5:
106 | orientation = UIImageOrientationLeftMirrored;
107 | break;
108 |
109 | case 7:
110 | orientation = UIImageOrientationRightMirrored;
111 | break;
112 | default:
113 | break;
114 | }
115 | return orientation;
116 | }
117 | #endif
118 |
119 | - (nullable NSData *)sd_imageData {
120 | return [self sd_imageDataAsFormat:SDImageFormatUndefined];
121 | }
122 |
123 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {
124 | NSData *imageData = nil;
125 | if (self) {
126 | #if SD_UIKIT || SD_WATCH
127 | int alphaInfo = CGImageGetAlphaInfo(self.CGImage);
128 | BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
129 | alphaInfo == kCGImageAlphaNoneSkipFirst ||
130 | alphaInfo == kCGImageAlphaNoneSkipLast);
131 |
132 | BOOL usePNG = hasAlpha;
133 |
134 | // the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel
135 | if (imageFormat != SDImageFormatUndefined) {
136 | usePNG = (imageFormat == SDImageFormatPNG);
137 | }
138 |
139 | if (usePNG) {
140 | imageData = UIImagePNGRepresentation(self);
141 | } else {
142 | imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);
143 | }
144 | #else
145 | NSBitmapImageFileType imageFileType = NSJPEGFileType;
146 | if (imageFormat == SDImageFormatGIF) {
147 | imageFileType = NSGIFFileType;
148 | } else if (imageFormat == SDImageFormatPNG) {
149 | imageFileType = NSPNGFileType;
150 | }
151 |
152 | imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations
153 | usingType:imageFileType
154 | properties:@{}];
155 | #endif
156 | }
157 | return imageData;
158 | }
159 |
160 |
161 | @end
162 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | /**
16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state.
17 | */
18 | @interface UIImageView (HighlightedWebCache)
19 |
20 | /**
21 | * Set the imageView `highlightedImage` with an `url`.
22 | *
23 | * The download is asynchronous and cached.
24 | *
25 | * @param url The url for the image.
26 | */
27 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
28 |
29 | /**
30 | * Set the imageView `highlightedImage` with an `url` and custom options.
31 | *
32 | * The download is asynchronous and cached.
33 | *
34 | * @param url The url for the image.
35 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
36 | */
37 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
38 | options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
39 |
40 | /**
41 | * Set the imageView `highlightedImage` with an `url`.
42 | *
43 | * The download is asynchronous and cached.
44 | *
45 | * @param url The url for the image.
46 | * @param completedBlock A block called when operation has been completed. This block has no return value
47 | * and takes the requested UIImage as first parameter. In case of error the image parameter
48 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
49 | * indicating if the image was retrieved from the local cache or from the network.
50 | * The fourth parameter is the original image url.
51 | */
52 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
53 | completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
54 |
55 | /**
56 | * Set the imageView `highlightedImage` with an `url` and custom options.
57 | *
58 | * The download is asynchronous and cached.
59 | *
60 | * @param url The url for the image.
61 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
62 | * @param completedBlock A block called when operation has been completed. This block has no return value
63 | * and takes the requested UIImage as first parameter. In case of error the image parameter
64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
65 | * indicating if the image was retrieved from the local cache or from the network.
66 | * The fourth parameter is the original image url.
67 | */
68 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
69 | options:(SDWebImageOptions)options
70 | completed:(nullable SDExternalCompletionBlock)completedBlock;
71 |
72 | /**
73 | * Set the imageView `highlightedImage` with an `url` and custom options.
74 | *
75 | * The download is asynchronous and cached.
76 | *
77 | * @param url The url for the image.
78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
79 | * @param progressBlock A block called while image is downloading
80 | * @note the progress block is executed on a background queue
81 | * @param completedBlock A block called when operation has been completed. This block has no return value
82 | * and takes the requested UIImage as first parameter. In case of error the image parameter
83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
84 | * indicating if the image was retrieved from the local cache or from the network.
85 | * The fourth parameter is the original image url.
86 | */
87 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
88 | options:(SDWebImageOptions)options
89 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
90 | completed:(nullable SDExternalCompletionBlock)completedBlock;
91 |
92 | @end
93 |
94 | #endif
95 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImageView+HighlightedWebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImageView+HighlightedWebCache.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "UIView+WebCacheOperation.h"
14 | #import "UIView+WebCache.h"
15 |
16 | @implementation UIImageView (HighlightedWebCache)
17 |
18 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url {
19 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil];
20 | }
21 |
22 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options {
23 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil];
24 | }
25 |
26 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
27 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock];
28 | }
29 |
30 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
31 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock];
32 | }
33 |
34 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
35 | options:(SDWebImageOptions)options
36 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
37 | completed:(nullable SDExternalCompletionBlock)completedBlock {
38 | __weak typeof(self)weakSelf = self;
39 | [self sd_internalSetImageWithURL:url
40 | placeholderImage:nil
41 | options:options
42 | operationKey:@"UIImageViewImageOperationHighlighted"
43 | setImageBlock:^(UIImage *image, NSData *imageData) {
44 | weakSelf.highlightedImage = image;
45 | }
46 | progress:progressBlock
47 | completed:completedBlock];
48 | }
49 |
50 | @end
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | /**
16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView.
17 | *
18 | * Usage with a UITableViewCell sub-class:
19 | *
20 | * @code
21 |
22 | #import
23 |
24 | ...
25 |
26 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
27 | {
28 | static NSString *MyIdentifier = @"MyIdentifier";
29 |
30 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
31 |
32 | if (cell == nil) {
33 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier]
34 | autorelease];
35 | }
36 |
37 | // Here we use the provided sd_setImageWithURL: method to load the web image
38 | // Ensure you use a placeholder image otherwise cells will be initialized with no image
39 | [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"]
40 | placeholderImage:[UIImage imageNamed:@"placeholder"]];
41 |
42 | cell.textLabel.text = @"My Text";
43 | return cell;
44 | }
45 |
46 | * @endcode
47 | */
48 | @interface UIImageView (WebCache)
49 |
50 | /**
51 | * Set the imageView `image` with an `url`.
52 | *
53 | * The download is asynchronous and cached.
54 | *
55 | * @param url The url for the image.
56 | */
57 | - (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
58 |
59 | /**
60 | * Set the imageView `image` with an `url` and a placeholder.
61 | *
62 | * The download is asynchronous and cached.
63 | *
64 | * @param url The url for the image.
65 | * @param placeholder The image to be set initially, until the image request finishes.
66 | * @see sd_setImageWithURL:placeholderImage:options:
67 | */
68 | - (void)sd_setImageWithURL:(nullable NSURL *)url
69 | placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
70 |
71 | /**
72 | * Set the imageView `image` with an `url`, placeholder and custom options.
73 | *
74 | * The download is asynchronous and cached.
75 | *
76 | * @param url The url for the image.
77 | * @param placeholder The image to be set initially, until the image request finishes.
78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
79 | */
80 | - (void)sd_setImageWithURL:(nullable NSURL *)url
81 | placeholderImage:(nullable UIImage *)placeholder
82 | options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
83 |
84 | /**
85 | * Set the imageView `image` with an `url`.
86 | *
87 | * The download is asynchronous and cached.
88 | *
89 | * @param url The url for the image.
90 | * @param completedBlock A block called when operation has been completed. This block has no return value
91 | * and takes the requested UIImage as first parameter. In case of error the image parameter
92 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
93 | * indicating if the image was retrieved from the local cache or from the network.
94 | * The fourth parameter is the original image url.
95 | */
96 | - (void)sd_setImageWithURL:(nullable NSURL *)url
97 | completed:(nullable SDExternalCompletionBlock)completedBlock;
98 |
99 | /**
100 | * Set the imageView `image` with an `url`, placeholder.
101 | *
102 | * The download is asynchronous and cached.
103 | *
104 | * @param url The url for the image.
105 | * @param placeholder The image to be set initially, until the image request finishes.
106 | * @param completedBlock A block called when operation has been completed. This block has no return value
107 | * and takes the requested UIImage as first parameter. In case of error the image parameter
108 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
109 | * indicating if the image was retrieved from the local cache or from the network.
110 | * The fourth parameter is the original image url.
111 | */
112 | - (void)sd_setImageWithURL:(nullable NSURL *)url
113 | placeholderImage:(nullable UIImage *)placeholder
114 | completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
115 |
116 | /**
117 | * Set the imageView `image` with an `url`, placeholder and custom options.
118 | *
119 | * The download is asynchronous and cached.
120 | *
121 | * @param url The url for the image.
122 | * @param placeholder The image to be set initially, until the image request finishes.
123 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
124 | * @param completedBlock A block called when operation has been completed. This block has no return value
125 | * and takes the requested UIImage as first parameter. In case of error the image parameter
126 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
127 | * indicating if the image was retrieved from the local cache or from the network.
128 | * The fourth parameter is the original image url.
129 | */
130 | - (void)sd_setImageWithURL:(nullable NSURL *)url
131 | placeholderImage:(nullable UIImage *)placeholder
132 | options:(SDWebImageOptions)options
133 | completed:(nullable SDExternalCompletionBlock)completedBlock;
134 |
135 | /**
136 | * Set the imageView `image` with an `url`, placeholder and custom options.
137 | *
138 | * The download is asynchronous and cached.
139 | *
140 | * @param url The url for the image.
141 | * @param placeholder The image to be set initially, until the image request finishes.
142 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
143 | * @param progressBlock A block called while image is downloading
144 | * @note the progress block is executed on a background queue
145 | * @param completedBlock A block called when operation has been completed. This block has no return value
146 | * and takes the requested UIImage as first parameter. In case of error the image parameter
147 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
148 | * indicating if the image was retrieved from the local cache or from the network.
149 | * The fourth parameter is the original image url.
150 | */
151 | - (void)sd_setImageWithURL:(nullable NSURL *)url
152 | placeholderImage:(nullable UIImage *)placeholder
153 | options:(SDWebImageOptions)options
154 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
155 | completed:(nullable SDExternalCompletionBlock)completedBlock;
156 |
157 | /**
158 | * Set the imageView `image` with an `url` and optionally a placeholder image.
159 | *
160 | * The download is asynchronous and cached.
161 | *
162 | * @param url The url for the image.
163 | * @param placeholder The image to be set initially, until the image request finishes.
164 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
165 | * @param progressBlock A block called while image is downloading
166 | * @note the progress block is executed on a background queue
167 | * @param completedBlock A block called when operation has been completed. This block has no return value
168 | * and takes the requested UIImage as first parameter. In case of error the image parameter
169 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
170 | * indicating if the image was retrieved from the local cache or from the network.
171 | * The fourth parameter is the original image url.
172 | */
173 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
174 | placeholderImage:(nullable UIImage *)placeholder
175 | options:(SDWebImageOptions)options
176 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
177 | completed:(nullable SDExternalCompletionBlock)completedBlock;
178 |
179 | #if SD_UIKIT
180 |
181 | #pragma mark - Animation of multiple images
182 |
183 | /**
184 | * Download an array of images and starts them in an animation loop
185 | *
186 | * @param arrayOfURLs An array of NSURL
187 | */
188 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs;
189 |
190 | - (void)sd_cancelCurrentAnimationImagesLoad;
191 |
192 | #endif
193 |
194 | @end
195 |
196 | #endif
197 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIImageView+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImageView+WebCache.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 | #import "UIView+WebCache.h"
16 |
17 | @implementation UIImageView (WebCache)
18 |
19 | - (void)sd_setImageWithURL:(nullable NSURL *)url {
20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
21 | }
22 |
23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
25 | }
26 |
27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
29 | }
30 |
31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
33 | }
34 |
35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
37 | }
38 |
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
40 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
41 | }
42 |
43 | - (void)sd_setImageWithURL:(nullable NSURL *)url
44 | placeholderImage:(nullable UIImage *)placeholder
45 | options:(SDWebImageOptions)options
46 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
47 | completed:(nullable SDExternalCompletionBlock)completedBlock {
48 | [self sd_internalSetImageWithURL:url
49 | placeholderImage:placeholder
50 | options:options
51 | operationKey:nil
52 | setImageBlock:nil
53 | progress:progressBlock
54 | completed:completedBlock];
55 | }
56 |
57 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
58 | placeholderImage:(nullable UIImage *)placeholder
59 | options:(SDWebImageOptions)options
60 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
61 | completed:(nullable SDExternalCompletionBlock)completedBlock {
62 | NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
63 | UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];
64 |
65 | [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];
66 | }
67 |
68 | #if SD_UIKIT
69 |
70 | #pragma mark - Animation of multiple images
71 |
72 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs {
73 | [self sd_cancelCurrentAnimationImagesLoad];
74 | __weak __typeof(self)wself = self;
75 |
76 | NSMutableArray> *operationsArray = [[NSMutableArray alloc] init];
77 |
78 | [arrayOfURLs enumerateObjectsUsingBlock:^(NSURL *logoImageURL, NSUInteger idx, BOOL * _Nonnull stop) {
79 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
80 | if (!wself) return;
81 | dispatch_main_async_safe(^{
82 | __strong UIImageView *sself = wself;
83 | [sself stopAnimating];
84 | if (sself && image) {
85 | NSMutableArray *currentImages = [[sself animationImages] mutableCopy];
86 | if (!currentImages) {
87 | currentImages = [[NSMutableArray alloc] init];
88 | }
89 |
90 | // We know what index objects should be at when they are returned so
91 | // we will put the object at the index, filling any empty indexes
92 | // with the image that was returned too "early". These images will
93 | // be overwritten. (does not require additional sorting datastructure)
94 | while ([currentImages count] < idx) {
95 | [currentImages addObject:image];
96 | }
97 |
98 | currentImages[idx] = image;
99 |
100 | sself.animationImages = currentImages;
101 | [sself setNeedsLayout];
102 | }
103 | [sself startAnimating];
104 | });
105 | }];
106 | [operationsArray addObject:operation];
107 | }];
108 |
109 | [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
110 | }
111 |
112 | - (void)sd_cancelCurrentAnimationImagesLoad {
113 | [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
114 | }
115 | #endif
116 |
117 | @end
118 |
119 | #endif
120 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIView+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
16 |
17 | @interface UIView (WebCache)
18 |
19 | /**
20 | * Get the current image URL.
21 | *
22 | * Note that because of the limitations of categories this property can get out of sync
23 | * if you use setImage: directly.
24 | */
25 | - (nullable NSURL *)sd_imageURL;
26 |
27 | /**
28 | * Set the imageView `image` with an `url` and optionally a placeholder image.
29 | *
30 | * The download is asynchronous and cached.
31 | *
32 | * @param url The url for the image.
33 | * @param placeholder The image to be set initially, until the image request finishes.
34 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
35 | * @param operationKey A string to be used as the operation key. If nil, will use the class name
36 | * @param setImageBlock Block used for custom set image code
37 | * @param progressBlock A block called while image is downloading
38 | * @note the progress block is executed on a background queue
39 | * @param completedBlock A block called when operation has been completed. This block has no return value
40 | * and takes the requested UIImage as first parameter. In case of error the image parameter
41 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
42 | * indicating if the image was retrieved from the local cache or from the network.
43 | * The fourth parameter is the original image url.
44 | */
45 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
46 | placeholderImage:(nullable UIImage *)placeholder
47 | options:(SDWebImageOptions)options
48 | operationKey:(nullable NSString *)operationKey
49 | setImageBlock:(nullable SDSetImageBlock)setImageBlock
50 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
51 | completed:(nullable SDExternalCompletionBlock)completedBlock;
52 |
53 | /**
54 | * Cancel the current download
55 | */
56 | - (void)sd_cancelCurrentImageLoad;
57 |
58 | #if SD_UIKIT
59 |
60 | #pragma mark - Activity indicator
61 |
62 | /**
63 | * Show activity UIActivityIndicatorView
64 | */
65 | - (void)sd_setShowActivityIndicatorView:(BOOL)show;
66 |
67 | /**
68 | * set desired UIActivityIndicatorViewStyle
69 | *
70 | * @param style The style of the UIActivityIndicatorView
71 | */
72 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
73 |
74 | - (BOOL)sd_showActivityIndicatorView;
75 | - (void)sd_addActivityIndicator;
76 | - (void)sd_removeActivityIndicator;
77 |
78 | #endif
79 |
80 | @end
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIView+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIView+WebCache.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 |
16 | static char imageURLKey;
17 |
18 | #if SD_UIKIT
19 | static char TAG_ACTIVITY_INDICATOR;
20 | static char TAG_ACTIVITY_STYLE;
21 | #endif
22 | static char TAG_ACTIVITY_SHOW;
23 |
24 | @implementation UIView (WebCache)
25 |
26 | - (nullable NSURL *)sd_imageURL {
27 | return objc_getAssociatedObject(self, &imageURLKey);
28 | }
29 |
30 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
31 | placeholderImage:(nullable UIImage *)placeholder
32 | options:(SDWebImageOptions)options
33 | operationKey:(nullable NSString *)operationKey
34 | setImageBlock:(nullable SDSetImageBlock)setImageBlock
35 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
36 | completed:(nullable SDExternalCompletionBlock)completedBlock {
37 | NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
38 | [self sd_cancelImageLoadOperationWithKey:validOperationKey];
39 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
40 |
41 | if (!(options & SDWebImageDelayPlaceholder)) {
42 | dispatch_main_async_safe(^{
43 | [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
44 | });
45 | }
46 |
47 | if (url) {
48 | // check if activityView is enabled or not
49 | if ([self sd_showActivityIndicatorView]) {
50 | [self sd_addActivityIndicator];
51 | }
52 |
53 | __weak __typeof(self)wself = self;
54 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
55 | __strong __typeof (wself) sself = wself;
56 | [sself sd_removeActivityIndicator];
57 | if (!sself) {
58 | return;
59 | }
60 | dispatch_main_async_safe(^{
61 | if (!sself) {
62 | return;
63 | }
64 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
65 | completedBlock(image, error, cacheType, url);
66 | return;
67 | } else if (image) {
68 | [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
69 | [sself sd_setNeedsLayout];
70 | } else {
71 | if ((options & SDWebImageDelayPlaceholder)) {
72 | [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
73 | [sself sd_setNeedsLayout];
74 | }
75 | }
76 | if (completedBlock && finished) {
77 | completedBlock(image, error, cacheType, url);
78 | }
79 | });
80 | }];
81 | [self sd_setImageLoadOperation:operation forKey:validOperationKey];
82 | } else {
83 | dispatch_main_async_safe(^{
84 | [self sd_removeActivityIndicator];
85 | if (completedBlock) {
86 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
87 | completedBlock(nil, error, SDImageCacheTypeNone, url);
88 | }
89 | });
90 | }
91 | }
92 |
93 | - (void)sd_cancelCurrentImageLoad {
94 | [self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
95 | }
96 |
97 | - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
98 | if (setImageBlock) {
99 | setImageBlock(image, imageData);
100 | return;
101 | }
102 |
103 | #if SD_UIKIT || SD_MAC
104 | if ([self isKindOfClass:[UIImageView class]]) {
105 | UIImageView *imageView = (UIImageView *)self;
106 | imageView.image = image;
107 | }
108 | #endif
109 |
110 | #if SD_UIKIT
111 | if ([self isKindOfClass:[UIButton class]]) {
112 | UIButton *button = (UIButton *)self;
113 | [button setImage:image forState:UIControlStateNormal];
114 | }
115 | #endif
116 | }
117 |
118 | - (void)sd_setNeedsLayout {
119 | #if SD_UIKIT
120 | [self setNeedsLayout];
121 | #elif SD_MAC
122 | [self setNeedsLayout:YES];
123 | #endif
124 | }
125 |
126 | #pragma mark - Activity indicator
127 |
128 | #pragma mark -
129 | #if SD_UIKIT
130 | - (UIActivityIndicatorView *)activityIndicator {
131 | return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
132 | }
133 |
134 | - (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
135 | objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
136 | }
137 | #endif
138 |
139 | - (void)sd_setShowActivityIndicatorView:(BOOL)show {
140 | objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
141 | }
142 |
143 | - (BOOL)sd_showActivityIndicatorView {
144 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
145 | }
146 |
147 | #if SD_UIKIT
148 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
149 | objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
150 | }
151 |
152 | - (int)sd_getIndicatorStyle{
153 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
154 | }
155 | #endif
156 |
157 | - (void)sd_addActivityIndicator {
158 | #if SD_UIKIT
159 | dispatch_main_async_safe(^{
160 | if (!self.activityIndicator) {
161 | self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self sd_getIndicatorStyle]];
162 | self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
163 |
164 | [self addSubview:self.activityIndicator];
165 |
166 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
167 | attribute:NSLayoutAttributeCenterX
168 | relatedBy:NSLayoutRelationEqual
169 | toItem:self
170 | attribute:NSLayoutAttributeCenterX
171 | multiplier:1.0
172 | constant:0.0]];
173 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
174 | attribute:NSLayoutAttributeCenterY
175 | relatedBy:NSLayoutRelationEqual
176 | toItem:self
177 | attribute:NSLayoutAttributeCenterY
178 | multiplier:1.0
179 | constant:0.0]];
180 | }
181 | [self.activityIndicator startAnimating];
182 | });
183 | #endif
184 | }
185 |
186 | - (void)sd_removeActivityIndicator {
187 | #if SD_UIKIT
188 | dispatch_main_async_safe(^{
189 | if (self.activityIndicator) {
190 | [self.activityIndicator removeFromSuperview];
191 | self.activityIndicator = nil;
192 | }
193 | });
194 | #endif
195 | }
196 |
197 | @end
198 |
199 | #endif
200 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | @interface UIView (WebCacheOperation)
16 |
17 | /**
18 | * Set the image load operation (storage in a UIView based dictionary)
19 | *
20 | * @param operation the operation
21 | * @param key key for storing the operation
22 | */
23 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key;
24 |
25 | /**
26 | * Cancel all operations for the current UIView and key
27 | *
28 | * @param key key for identifying the operations
29 | */
30 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key;
31 |
32 | /**
33 | * Just remove the operations corresponding to the current UIView and key without cancelling them
34 | *
35 | * @param key key for identifying the operations
36 | */
37 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key;
38 |
39 | @end
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/SDWebImage/SDWebImage/UIView+WebCacheOperation.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIView+WebCacheOperation.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 |
15 | static char loadOperationKey;
16 |
17 | typedef NSMutableDictionary SDOperationsDictionary;
18 |
19 | @implementation UIView (WebCacheOperation)
20 |
21 | - (SDOperationsDictionary *)operationDictionary {
22 | SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
23 | if (operations) {
24 | return operations;
25 | }
26 | operations = [NSMutableDictionary dictionary];
27 | objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
28 | return operations;
29 | }
30 |
31 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
32 | if (key) {
33 | [self sd_cancelImageLoadOperationWithKey:key];
34 | if (operation) {
35 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
36 | operationDictionary[key] = operation;
37 | }
38 | }
39 | }
40 |
41 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
42 | // Cancel in progress downloader from queue
43 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
44 | id operations = operationDictionary[key];
45 | if (operations) {
46 | if ([operations isKindOfClass:[NSArray class]]) {
47 | for (id operation in operations) {
48 | if (operation) {
49 | [operation cancel];
50 | }
51 | }
52 | } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
53 | [(id) operations cancel];
54 | }
55 | [operationDictionary removeObjectForKey:key];
56 | }
57 | }
58 |
59 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key {
60 | if (key) {
61 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
62 | [operationDictionary removeObjectForKey:key];
63 | }
64 | }
65 |
66 | @end
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## SDWebImage
5 |
6 | Copyright (c) 2009-2017 Olivier Poitrey rs@dailymotion.com
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is furnished
13 | to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in all
16 | copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 |
27 | Generated by CocoaPods - https://cocoapods.org
28 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2009-2017 Olivier Poitrey rs@dailymotion.com
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is furnished
24 | to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in all
27 | copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 |
38 | License
39 | MIT
40 | Title
41 | SDWebImage
42 | Type
43 | PSGroupSpecifier
44 |
45 |
46 | FooterText
47 | Generated by CocoaPods - https://cocoapods.org
48 | Title
49 |
50 | Type
51 | PSGroupSpecifier
52 |
53 |
54 | StringsTable
55 | Acknowledgements
56 | Title
57 | Acknowledgements
58 |
59 |
60 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_MVVMPlayground : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_MVVMPlayground
5 | @end
6 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 |
104 | if [[ "$CONFIGURATION" == "Debug" ]]; then
105 | install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
106 | fi
107 | if [[ "$CONFIGURATION" == "Release" ]]; then
108 | install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
109 | fi
110 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
111 | wait
112 | fi
113 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_MVVMPlaygroundVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_MVVMPlaygroundVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground.debug.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers"
5 | OTHER_LDFLAGS = $(inherited) -framework "SDWebImage"
6 | PODS_BUILD_DIR = $BUILD_DIR
7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_MVVMPlayground {
2 | umbrella header "Pods-MVVMPlayground-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlayground/Pods-MVVMPlayground.release.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers"
5 | OTHER_LDFLAGS = $(inherited) -framework "SDWebImage"
6 | PODS_BUILD_DIR = $BUILD_DIR
7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 | Generated by CocoaPods - https://cocoapods.org
4 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Generated by CocoaPods - https://cocoapods.org
18 | Title
19 |
20 | Type
21 | PSGroupSpecifier
22 |
23 |
24 | StringsTable
25 | Acknowledgements
26 | Title
27 | Acknowledgements
28 |
29 |
30 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_MVVMPlaygroundTests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_MVVMPlaygroundTests
5 | @end
6 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
104 | wait
105 | fi
106 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_MVVMPlaygroundTestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_MVVMPlaygroundTestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_MVVMPlaygroundTests {
2 | umbrella header "Pods-MVVMPlaygroundTests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/Pods-MVVMPlaygroundTests/Pods-MVVMPlaygroundTests.release.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 4.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/SDWebImage-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_SDWebImage : NSObject
3 | @end
4 | @implementation PodsDummy_SDWebImage
5 | @end
6 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/SDWebImage-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/SDWebImage-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | #import "NSData+ImageContentType.h"
14 | #import "NSImage+WebCache.h"
15 | #import "SDImageCache.h"
16 | #import "SDImageCacheConfig.h"
17 | #import "SDWebImageCompat.h"
18 | #import "SDWebImageDecoder.h"
19 | #import "SDWebImageDownloader.h"
20 | #import "SDWebImageDownloaderOperation.h"
21 | #import "SDWebImageManager.h"
22 | #import "SDWebImageOperation.h"
23 | #import "SDWebImagePrefetcher.h"
24 | #import "UIButton+WebCache.h"
25 | #import "UIImage+GIF.h"
26 | #import "UIImage+MultiFormat.h"
27 | #import "UIImageView+HighlightedWebCache.h"
28 | #import "UIImageView+WebCache.h"
29 | #import "UIView+WebCache.h"
30 | #import "UIView+WebCacheOperation.h"
31 |
32 | FOUNDATION_EXPORT double SDWebImageVersionNumber;
33 | FOUNDATION_EXPORT const unsigned char SDWebImageVersionString[];
34 |
35 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/SDWebImage.modulemap:
--------------------------------------------------------------------------------
1 | framework module SDWebImage {
2 | umbrella header "SDWebImage-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/MVVMPlayground/Pods/Target Support Files/SDWebImage/SDWebImage.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SDWebImage
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_LDFLAGS = -framework "ImageIO"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SDWebImage
9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
10 | SKIP_INSTALL = YES
11 |
--------------------------------------------------------------------------------
/MVVMPlayground/readme.md:
--------------------------------------------------------------------------------
1 | # MVVM Playground, a photo showcase app.
2 |
3 | This is a sample code for MVVM playground.
4 |
5 | Please refer to tag 'MVC' to find the MVC version of the app.
6 | And then please checke the latest code for the MVVM version with unit tests, it's cool!
7 |
8 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 | import XCTest
5 | import PlaygroundSupport
6 | import Foundation
7 |
8 | // Protocol for MOCK/Real
9 | protocol URLSessionProtocol {
10 | typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
11 |
12 | func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
13 | }
14 |
15 | protocol URLSessionDataTaskProtocol {
16 | func resume()
17 | }
18 |
19 | //MARK: HttpClient Implementation
20 | class HttpClient {
21 |
22 | typealias completeClosure = ( _ data: Data?, _ error: Error?)->Void
23 |
24 | private let session: URLSessionProtocol
25 |
26 | init(session: URLSessionProtocol) {
27 | self.session = session
28 |
29 | }
30 |
31 | func get( url: URL, callback: @escaping completeClosure ) {
32 | var request = URLRequest(url: url)
33 | request.httpMethod = "GET"
34 | let task = session.dataTask(with: request) { (data, response, error) in
35 | callback(data, error)
36 | }
37 | task.resume()
38 | }
39 |
40 | }
41 |
42 | //MARK: Conform the protocol
43 | extension URLSession: URLSessionProtocol {
44 | func dataTask(with request: URLRequest, completionHandler: @escaping URLSessionProtocol.DataTaskResult) -> URLSessionDataTaskProtocol {
45 | return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask
46 | }
47 | }
48 |
49 | extension URLSessionDataTask: URLSessionDataTaskProtocol {}
50 |
51 | //MARK: MOCK
52 | class MockURLSession: URLSessionProtocol {
53 |
54 | var nextDataTask = MockURLSessionDataTask()
55 | var nextData: Data?
56 | var nextError: Error?
57 |
58 | private (set) var lastURL: URL?
59 |
60 | func successHttpURLResponse(request: URLRequest) -> URLResponse {
61 | return HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: nil)!
62 | }
63 |
64 | func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
65 | lastURL = request.url
66 |
67 | completionHandler(nextData, successHttpURLResponse(request: request), nextError)
68 | return nextDataTask
69 | }
70 |
71 | }
72 |
73 | class MockURLSessionDataTask: URLSessionDataTaskProtocol {
74 | private (set) var resumeWasCalled = false
75 |
76 | func resume() {
77 | resumeWasCalled = true
78 | }
79 | }
80 |
81 | //MARK: Test
82 | class HttpClientTests: XCTestCase {
83 |
84 | var httpClient: HttpClient!
85 | let session = MockURLSession()
86 |
87 | override func setUp() {
88 | super.setUp()
89 | httpClient = HttpClient(session: session)
90 | }
91 |
92 | override func tearDown() {
93 | super.tearDown()
94 | }
95 |
96 | func test_get_request_with_URL() {
97 |
98 | guard let url = URL(string: "https://mockurl") else {
99 | fatalError("URL can't be empty")
100 | }
101 |
102 | httpClient.get(url: url) { (success, response) in
103 | // Return data
104 | }
105 |
106 | XCTAssert(session.lastURL == url)
107 |
108 | }
109 |
110 | func test_get_resume_called() {
111 |
112 | let dataTask = MockURLSessionDataTask()
113 | session.nextDataTask = dataTask
114 |
115 | guard let url = URL(string: "https://mockurl") else {
116 | fatalError("URL can't be empty")
117 | }
118 |
119 | httpClient.get(url: url) { (success, response) in
120 | // Return data
121 | }
122 |
123 | XCTAssert(dataTask.resumeWasCalled)
124 | }
125 |
126 | func test_get_should_return_data() {
127 | let expectedData = "{}".data(using: .utf8)
128 |
129 | session.nextData = expectedData
130 |
131 | var actualData: Data?
132 | httpClient.get(url: URL(string: "http://mockurl")!) { (data, error) in
133 | actualData = data
134 | }
135 |
136 | XCTAssertNotNil(actualData)
137 | }
138 |
139 | }
140 |
141 | HttpClientTests.defaultTestSuite.run()
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/Resources/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "userId": 1,
3 | "id": 1,
4 | "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
5 | "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
6 | }
7 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koromiko/Tutorial/d3c691df8a00237f24ba52a428ff194a151de134/NetworkingUnitTest.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | EnabledFullIndexStoreVisibility
12 |
13 | IssueFilterStyle
14 | ShowActiveSchemeOnly
15 | LiveSourceIssuesEnabled
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/NetworkingUnitTest.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList.xcodeproj/project.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koromiko/Tutorial/d3c691df8a00237f24ba52a428ff194a151de134/PersistentTodoList/PersistentTodoList.xcodeproj/project.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList.xcodeproj/xcuserdata/neo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/PersistentTodoList.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList.xcodeproj/xcuserdata/neo.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | PersistentTodoList.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | B57D8AD21F5A4C3B002725DA
16 |
17 | primary
18 |
19 |
20 | B57D8AE91F5A4C3B002725DA
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // PersistentTodoList
4 | //
5 | // Created by Neo on 02/09/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
19 | // Override point for customization after application launch.
20 |
21 | return true
22 | }
23 |
24 | func applicationWillResignActive(_ application: UIApplication) {
25 | // 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.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 | func applicationDidEnterBackground(_ application: UIApplication) {
30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
32 | }
33 |
34 | func applicationWillEnterForeground(_ application: UIApplication) {
35 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
36 | }
37 |
38 | func applicationDidBecomeActive(_ application: UIApplication) {
39 | // 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.
40 | }
41 |
42 | func applicationWillTerminate(_ application: UIApplication) {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | // Saves changes in the application's managed object context before the application terminates.
45 | self.saveContext()
46 | }
47 |
48 | // MARK: - Core Data stack
49 | lazy var persistentContainer: NSPersistentContainer = {
50 | let container = NSPersistentContainer(name: "PersistentTodoList")
51 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in
52 | if let error = error as NSError? {
53 | fatalError("Unresolved error \(error), \(error.userInfo)")
54 | }
55 | })
56 | return container
57 | }()
58 |
59 | // MARK: - Core Data Saving support
60 | func saveContext () {
61 | let context = persistentContainer.viewContext
62 | if context.hasChanges {
63 | do {
64 | try context.save()
65 | } catch {
66 | let nserror = error as NSError
67 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
68 | }
69 | }
70 | }
71 |
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/PersistentTodoList.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | PersistentTodoList.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/PersistentTodoList.xcdatamodeld/PersistentTodoList.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/Storage/ToDoStorageManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToDoStorageManager.swift
3 | // PersistentTodoList
4 | //
5 | // Created by Neo on 02/09/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreData
11 | import UIKit
12 |
13 | class ToDoStorgeManager {
14 |
15 | let persistentContainer: NSPersistentContainer!
16 |
17 | lazy var backgroundContext: NSManagedObjectContext = {
18 | return self.persistentContainer.newBackgroundContext()
19 | }()
20 |
21 | //MARK: Init with dependency
22 | init(container: NSPersistentContainer) {
23 | self.persistentContainer = container
24 | self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
25 | }
26 |
27 | convenience init() {
28 | //Use the default container for production environment
29 | guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
30 | fatalError("Can not get shared app delegate")
31 | }
32 | self.init(container: appDelegate.persistentContainer)
33 | }
34 |
35 | //MARK: CRUD
36 | func insertTodoItem( name: String, finished: Bool ) -> ToDoItem? {
37 |
38 | guard let toDoItem = NSEntityDescription.insertNewObject(forEntityName: "ToDoItem", into: backgroundContext) as? ToDoItem else { return nil }
39 | toDoItem.name = name
40 | toDoItem.finished = finished
41 |
42 | return toDoItem
43 | }
44 |
45 | func fetchAll() -> [ToDoItem] {
46 | let request: NSFetchRequest = ToDoItem.fetchRequest()
47 | let results = try? persistentContainer.viewContext.fetch(request)
48 | return results ?? [ToDoItem]()
49 | }
50 |
51 | func remove( objectID: NSManagedObjectID ) {
52 | let obj = backgroundContext.object(with: objectID)
53 | backgroundContext.delete(obj)
54 | }
55 |
56 | func save() {
57 | if backgroundContext.hasChanges {
58 | do {
59 | try backgroundContext.save()
60 | } catch {
61 | print("Save error \(error)")
62 | }
63 | }
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoList/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // PersistentTodoList
4 | //
5 | // Created by Neo on 02/09/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 |
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoListTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/PersistentTodoList/PersistentTodoListTests/PersistentTodoListTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersistentTodoListTests.swift
3 | // PersistentTodoListTests
4 | //
5 | // Created by Neo on 02/09/2017.
6 | // Copyright © 2017 ST.Huang. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import CoreData
11 |
12 | @testable import PersistentTodoList
13 |
14 | class PersistentTodoListTests: XCTestCase {
15 |
16 | var sut: ToDoStorgeManager!
17 |
18 | override func setUp() {
19 | super.setUp()
20 |
21 | initStubs()
22 |
23 | sut = ToDoStorgeManager(container: mockPersistantContainer)
24 |
25 |
26 | //Listen to the change in context
27 | NotificationCenter.default.addObserver(self, selector: #selector(contextSaved(notification:)), name: NSNotification.Name.NSManagedObjectContextDidSave , object: nil)
28 |
29 | }
30 |
31 | override func tearDown() {
32 | NotificationCenter.default.removeObserver(self)
33 |
34 | flushData()
35 |
36 | super.tearDown()
37 | }
38 |
39 | func test_create_todo() {
40 |
41 | //Given the name & status
42 | let name = "Todo1"
43 | let finished = false
44 |
45 | //When add todo
46 | let todo = sut.insertTodoItem(name: name, finished: finished)
47 |
48 | //Assert: return todo item
49 | XCTAssertNotNil( todo )
50 |
51 | }
52 |
53 | func test_fetch_all_todo() {
54 |
55 | //Given a storage with two todo
56 |
57 | //When fetch
58 | let results = sut.fetchAll()
59 |
60 | //Assert return two todo items
61 | XCTAssertEqual(results.count, 5)
62 | }
63 |
64 | func test_remove_todo() {
65 |
66 | //Given a item in persistent store
67 | let items = sut.fetchAll()
68 | let item = items[0]
69 |
70 | let numberOfItems = items.count
71 |
72 | //When remove a item
73 | sut.remove(objectID: item.objectID)
74 | sut.save()
75 |
76 | //Assert number of item - 1
77 | XCTAssertEqual(numberOfItemsInPersistentStore(), numberOfItems-1)
78 |
79 | }
80 |
81 | func test_save() {
82 |
83 | //Give a todo item
84 | let name = "Todo1"
85 | let finished = false
86 |
87 | _ = expectationForSaveNotification()
88 |
89 | _ = sut.insertTodoItem(name: name, finished: finished)
90 |
91 | //When save
92 |
93 |
94 | //Assert save is called via notification (wait)
95 | expectation(forNotification: Notification.Name.NSManagedObjectContextDidSave.rawValue, object: nil, handler: nil)
96 |
97 | sut.save()
98 |
99 | waitForExpectations(timeout: 1.0, handler: nil)
100 |
101 |
102 | }
103 |
104 |
105 |
106 | //MARK: mock in-memory persistant store
107 | lazy var managedObjectModel: NSManagedObjectModel = {
108 | let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: type(of: self))] )!
109 | return managedObjectModel
110 | }()
111 |
112 | lazy var mockPersistantContainer: NSPersistentContainer = {
113 |
114 | let container = NSPersistentContainer(name: "PersistentTodoList", managedObjectModel: self.managedObjectModel)
115 | let description = NSPersistentStoreDescription()
116 | description.type = NSInMemoryStoreType
117 | description.shouldAddStoreAsynchronously = false // Make it simpler in test env
118 |
119 | container.persistentStoreDescriptions = [description]
120 | container.loadPersistentStores { (description, error) in
121 | // Check if the data store is in memory
122 | precondition( description.type == NSInMemoryStoreType )
123 |
124 | // Check if creating container wrong
125 | if let error = error {
126 | fatalError("Create an in-mem coordinator failed \(error)")
127 | }
128 | }
129 | return container
130 | }()
131 |
132 | //MARK: Convinient function for notification
133 | var saveNotificationCompleteHandler: ((Notification)->())?
134 |
135 | func expectationForSaveNotification() -> XCTestExpectation {
136 | let expect = expectation(description: "Context Saved")
137 | waitForSavedNotification { (notification) in
138 | expect.fulfill()
139 | }
140 | return expect
141 | }
142 |
143 | func waitForSavedNotification(completeHandler: @escaping ((Notification)->()) ) {
144 | saveNotificationCompleteHandler = completeHandler
145 | }
146 |
147 | func contextSaved( notification: Notification ) {
148 | print("\(notification)")
149 | saveNotificationCompleteHandler?(notification)
150 | }
151 | }
152 |
153 | //MARK: Creat some fakes
154 | extension PersistentTodoListTests {
155 |
156 | func initStubs() {
157 |
158 | func insertTodoItem( name: String, finished: Bool ) -> ToDoItem? {
159 |
160 | let obj = NSEntityDescription.insertNewObject(forEntityName: "ToDoItem", into: mockPersistantContainer.viewContext)
161 |
162 | obj.setValue("1", forKey: "name")
163 | obj.setValue(false, forKey: "finished")
164 |
165 | return obj as? ToDoItem
166 | }
167 |
168 | _ = insertTodoItem(name: "1", finished: false)
169 | _ = insertTodoItem(name: "2", finished: false)
170 | _ = insertTodoItem(name: "3", finished: false)
171 | _ = insertTodoItem(name: "4", finished: false)
172 | _ = insertTodoItem(name: "5", finished: false)
173 |
174 | do {
175 | try mockPersistantContainer.viewContext.save()
176 | } catch {
177 | print("create fakes error \(error)")
178 | }
179 |
180 | }
181 |
182 | func flushData() {
183 |
184 | let fetchRequest:NSFetchRequest = NSFetchRequest(entityName: "ToDoItem")
185 | let objs = try! mockPersistantContainer.viewContext.fetch(fetchRequest)
186 | for case let obj as NSManagedObject in objs {
187 | mockPersistantContainer.viewContext.delete(obj)
188 | }
189 |
190 | try! mockPersistantContainer.viewContext.save()
191 |
192 | }
193 |
194 | func numberOfItemsInPersistentStore() -> Int {
195 | let request: NSFetchRequest = NSFetchRequest(entityName: "ToDoItem")
196 | let results = try! mockPersistantContainer.viewContext.fetch(request)
197 | return results.count
198 | }
199 |
200 | }
201 |
202 |
203 |
204 |
205 |
--------------------------------------------------------------------------------
/Sequence.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 |
5 | struct Beer: CustomDebugStringConvertible {
6 | var brandName: String
7 | var volume: Int
8 |
9 | var debugDescription: String {
10 | return "\(brandName): \(volume) ml"
11 | }
12 |
13 | public var isCan: Bool {
14 | return (self.volume < 350)
15 | }
16 | }
17 |
18 |
19 | struct BeerContainer {
20 | let elements: [Beer]
21 | var i = 0
22 |
23 | init(elements: [Beer]) {
24 | self.elements = elements
25 | }
26 | }
27 |
28 | extension BeerContainer: IteratorProtocol {
29 | typealias Element = Beer
30 |
31 | mutating func next() -> Element? {
32 | defer {
33 | i+=1
34 | }
35 | return i
47 | //
48 | // func makeIterator() -> Iterator {
49 | // return elements.makeIterator()
50 | // }
51 | //
52 | //}
53 |
54 | // Try AnyIterator
55 | //extension VendorMachine: Sequence {
56 | // typealias Iterator = AnyIterator
57 | //
58 | // func makeIterator() -> Iterator {
59 | // var iterator = elements.makeIterator()
60 | // return AnyIterator {
61 | // iterator.next()
62 | // }
63 | // }
64 | //}
65 |
66 | // Use our Iterator
67 | //extension VendorMachine: Sequence {
68 | // typealias Iterator = BeerContainer
69 | //
70 | // func makeIterator() -> Iterator {
71 | // return BeerContainer(elements: self.elements)
72 | // }
73 | //}
74 |
75 | struct VendorMachine: Sequence {
76 | typealias Iterator = AnyIterator
77 |
78 | let elements: [Beer]
79 |
80 | func makeIterator() -> Iterator {
81 | var i = self.elements.startIndex
82 | return AnyIterator {
83 | defer {
84 | i+=1
85 | }
86 | return i$1.volume }
110 | print(sortedBeers)
111 |
112 | let largeBeers = aMachine.filter { $0.volume>400 }
113 | print( largeBeers )
114 |
115 | let totalVolume = aMachine.reduce(0) { return $0+$1.volume }
116 | print(totalVolume)
117 |
118 |
--------------------------------------------------------------------------------
/Sequence.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Sequence.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sequence.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koromiko/Tutorial/d3c691df8a00237f24ba52a428ff194a151de134/Sequence.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/UnitTestBasic.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 |
5 |
6 |
7 |
8 | //MARK: Test
9 |
10 | class HttpClientTests: XCTestCase {
11 |
12 | var httpClient: HttpClient!
13 | let session = MockURLSession()
14 |
15 | override func setUp() {
16 | super.setUp()
17 | httpClient = HttpClient(session: session)
18 |
19 | }
20 | override func tearDown() {
21 | super.tearDown()
22 | }
23 |
24 |
25 | func test_get_requestWithURL() {
26 | let dataTask = MockURLSessionDataTask()
27 | session.nextDataTask = dataTask
28 |
29 | let url = URL(string: "https://mockurl")
30 | httpClient.get(url: url) { (success, response) in
31 |
32 | }
33 |
34 | XCTAssert(session.lastURL == url)
35 | XCTAssert(dataTask.resumeWasCalled)
36 | }
37 |
38 | func test_get_returnData() {
39 | let expectedData = "{}".data(using: .utf8)
40 |
41 | session.nextData = expectedData
42 |
43 | var actualData: Data?
44 | httpClient.get(url: URL(string: "http://mockurl")!) { (data, error) in
45 | actualData = data
46 | }
47 |
48 | XCTAssertNotNil(actualData)
49 | }
50 |
51 | }
52 |
53 | HttpClientTests.defaultTestSuite().run()
--------------------------------------------------------------------------------
/UnitTestBasic.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/UnitTestBasic.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/UnitTestBasic.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koromiko/Tutorial/d3c691df8a00237f24ba52a428ff194a151de134/UnitTestBasic.playground/playground.xcworkspace/xcuserdata/neo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Sample code for S.T Hunag
2 |
3 |
4 | All articles for the sample code could be found in:
5 |
6 | 1. [S.T Huang](https://medium.com/@koromikoneo) (English)
7 | 2. [鍵盤藍綠藻Neo](https://koromiko1104.wordpress.com/) (Mandarin)
8 |
--------------------------------------------------------------------------------