├── .gitignore
├── LICENSE
├── Podfile
├── Podverse.xcodeproj
└── project.pbxproj
├── Podverse
├── AboutPlayingItemViewController.swift
├── AboutViewController.swift
├── AddToPlaylistViewController.swift
├── AppDelegate.swift
├── Array+rearrange.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── 114.png
│ │ ├── 120-1.png
│ │ ├── 120.png
│ │ ├── 180.png
│ │ ├── 29.png
│ │ ├── 57.png
│ │ ├── 58.png
│ │ ├── 80.png
│ │ ├── 87.png
│ │ ├── Contents.json
│ │ └── icon-1024x1024.png
│ ├── Blank52.imageset
│ │ ├── Blank52.png
│ │ ├── Blank52@2x.png
│ │ └── Contents.json
│ ├── Contents.json
│ ├── LaunchImage.launchimage
│ │ ├── 1125x2436.png
│ │ ├── 1242x2208.png
│ │ ├── 2208x1242.png
│ │ ├── 320x480.png
│ │ ├── 640x1136-1.png
│ │ ├── 640x1136.png
│ │ ├── 640x960-1.png
│ │ ├── 640x960.png
│ │ ├── 750x1334.png
│ │ └── Contents.json
│ ├── PodverseIcon.imageset
│ │ ├── Contents.json
│ │ ├── PodverseIcon.png
│ │ ├── PodverseIcon@2x.png
│ │ └── PodverseIcon@3x.png
│ ├── SliderClipPosition.imageset
│ │ ├── Contents.json
│ │ └── slider-clip-position.png
│ ├── SliderCurrentPosition.imageset
│ │ ├── Contents.json
│ │ └── slider-current-position.png
│ ├── Tab-Downloads.imageset
│ │ └── Contents.json
│ ├── Tab-Find.imageset
│ │ └── Contents.json
│ ├── Tab-Podcasts.imageset
│ │ └── Contents.json
│ ├── add.imageset
│ │ ├── Contents.json
│ │ ├── add.png
│ │ ├── add@2x.png
│ │ └── add@3x.png
│ ├── auto-dl.imageset
│ │ ├── Contents.json
│ │ ├── auto-dl.png
│ │ ├── auto-dl@2x.png
│ │ └── auto-dl@3x.png
│ ├── back15.imageset
│ │ ├── Contents.json
│ │ ├── back15-48px.png
│ │ ├── back15-72px.png
│ │ └── back15-96px.png
│ ├── banner-auth0.imageset
│ │ ├── Contents.json
│ │ └── banner-auth0.png
│ ├── banner.imageset
│ │ ├── Contents.json
│ │ ├── banner.png
│ │ ├── banner@2x.png
│ │ └── banner@3x.png
│ ├── clear.imageset
│ │ ├── Contents.json
│ │ ├── clear.png
│ │ ├── clear@2x.png
│ │ └── clear@3x.png
│ ├── clip.imageset
│ │ ├── Contents.json
│ │ ├── clip.png
│ │ ├── clip@2x.png
│ │ └── clip@3x.png
│ ├── cloud.imageset
│ │ ├── Contents.json
│ │ ├── cloud.png
│ │ ├── cloud@2x.png
│ │ └── cloud@3x.png
│ ├── drag
│ │ ├── Contents.json
│ │ ├── animation-1.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-1.tiff
│ │ ├── animation-10.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-10.tiff
│ │ ├── animation-11.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-11.tiff
│ │ ├── animation-12.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-12.tiff
│ │ ├── animation-13.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-13.tiff
│ │ ├── animation-14.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-14.tiff
│ │ ├── animation-15.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-15.tiff
│ │ ├── animation-16.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-16.tiff
│ │ ├── animation-17.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-17.tiff
│ │ ├── animation-18.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-18.tiff
│ │ ├── animation-19.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-19.tiff
│ │ ├── animation-2.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-2.tiff
│ │ ├── animation-20.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-20.tiff
│ │ ├── animation-3.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-3.tiff
│ │ ├── animation-4.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-4.tiff
│ │ ├── animation-5.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-5.tiff
│ │ ├── animation-6.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-6.tiff
│ │ ├── animation-7.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-7.tiff
│ │ ├── animation-8.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-8.tiff
│ │ └── animation-9.dataset
│ │ │ ├── Contents.json
│ │ │ └── animation-9.tiff
│ ├── forward15.imageset
│ │ ├── Contents.json
│ │ ├── forward15-48px.png
│ │ ├── forward15-72px.png
│ │ └── forward15-96px.png
│ ├── icon_grab.imageset
│ │ ├── Contents.json
│ │ ├── icon_grab@2x.png
│ │ └── icon_grab@3x.png
│ ├── pause.imageset
│ │ ├── Contents.json
│ │ ├── pause.png
│ │ ├── pause@2x.png
│ │ └── pause@3x.png
│ ├── play.imageset
│ │ ├── Contents.json
│ │ ├── play.png
│ │ ├── play@2x.png
│ │ └── play@3x.png
│ ├── playerror.imageset
│ │ ├── Contents.json
│ │ ├── playerror.png
│ │ ├── playerror@2x.png
│ │ └── playerror@3x.png
│ ├── skipbackward.imageset
│ │ ├── Contents.json
│ │ ├── skipbackward.png
│ │ ├── skipbackward@2x.png
│ │ └── skipbackward@3x.png
│ ├── skipforward.imageset
│ │ ├── Contents.json
│ │ ├── skipforward.png
│ │ ├── skipforward@2x.png
│ │ └── skipforward@3x.png
│ ├── speed05x.imageset
│ │ ├── 0_5x_48px.png
│ │ ├── 0_5x_72px.png
│ │ └── Contents.json
│ ├── speed075x.imageset
│ │ ├── 0_75x_48px.png
│ │ ├── 0_75x_72px.png
│ │ └── Contents.json
│ ├── speed125x.imageset
│ │ ├── 1_25x_48px.png
│ │ ├── 1_25x_72px.png
│ │ └── Contents.json
│ ├── speed15x.imageset
│ │ ├── 1_5x_48px.png
│ │ ├── 1_5x_72px.png
│ │ └── Contents.json
│ ├── speed1x.imageset
│ │ ├── 1x-72px.png
│ │ └── Contents.json
│ ├── speed2x.imageset
│ │ ├── 2x_48px.png
│ │ ├── 2x_72px.png
│ │ └── Contents.json
│ ├── tab-clips.imageset
│ │ ├── Contents.json
│ │ ├── tab-clips.png
│ │ ├── tab-clips@2x.png
│ │ └── tab-clips@3x.png
│ ├── tab-downloads.imageset
│ │ ├── tab-downloads.png
│ │ ├── tab-downloads@2x.png
│ │ └── tab-downloads@3x.png
│ ├── tab-find.imageset
│ │ ├── tab-find.png
│ │ ├── tab-find@2x.png
│ │ └── tab-find@3x.png
│ ├── tab-more.imageset
│ │ ├── Contents.json
│ │ ├── tab-more.png
│ │ ├── tab-more@2x.png
│ │ └── tab-more@3x.png
│ └── tab-podcasts.imageset
│ │ ├── tab-podcasts.png
│ │ ├── tab-podcasts@2x.png
│ │ └── tab-podcasts@3x.png
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ClipEpisodeTableViewCell.swift
├── ClipPodcastTableViewCell.swift
├── ClipTableViewCell.swift
├── ClipsListContainerViewController.swift
├── ClipsTableViewController.swift
├── Constants.swift
├── CoreDataHelper.swift
├── Data+ResizeImageData.swift
├── Date+FeedsDate.swift
├── Date+Formatters.swift
├── DeletingPodcasts.swift
├── DownloadTableViewCell.swift
├── DownloadingEpisode.swift
├── DownloadingEpisodeList.swift
├── DownloadsTableViewController.swift
├── Episode.swift
├── EpisodeTableViewCell.swift
├── EpisodeTableViewController.swift
├── EpisodesTableViewController.swift
├── EpisodesTableViewController.swift.orig
├── FiltersTableHeaderView.swift
├── FindBrowseGroupsViewController.swift
├── FindBrowsePodcastsViewController.swift
├── FindSearchTableViewController.swift
├── FindTableViewController.swift
├── Info.plist
├── Int64+MediaPlayerString.swift
├── LoginViewController.swift
├── MakeClipTimeViewController.swift
├── MediaPlayerViewController.swift
├── MediaRef.swift
├── MoreTableViewController.swift
├── NowPlayingBar.swift
├── NowPlayingBar.xib
├── PVAuth.swift
├── PVDeleter.swift
├── PVDownloader.swift
├── PVFeedParser.swift
├── PVMediaPlayer.swift
├── PVPlayerHistoryManager.swift
├── PVReachability.swift
├── PVSubscriber.swift
├── PVTimeHelpers.swift
├── PVViewController.swift
├── ParsingPodcasts.swift
├── PlayerHistoryItem.swift
├── Playlist.swift
├── PlaylistDetailTableViewCell.swift
├── PlaylistDetailTableViewController.swift
├── PlaylistTableViewCell.swift
├── PlaylistsTableViewController.swift
├── Podcast.swift
├── PodcastSearchResultTableViewCell.swift
├── PodcastTableViewCell.swift
├── PodcastsTableViewController.swift
├── Podverse.entitlements
├── SearchCategory.swift
├── SearchEpisode.swift
├── SearchNetwork.swift
├── SearchPodcast.swift
├── SearchPodcastViewController.swift
├── SettingsTableViewController.swift
├── String+CharacterManipulation.swift
├── String+ConvertPlaybackTimeToUrlSchemeElements.swift
├── String+Formatters.swift
├── String+MediaPlayerTimeToSeconds.swift
├── String+RemoveHTMLCharacters.swift
├── String+Utilities.swift
├── Supporting Libraries
│ └── OAuth2RetryHandler.swift
├── SyncablePodcast.swift
├── UITabbarController+PlayerView.swift
├── UITableView+NotAvailableView.swift
├── UIViewController+AllowCellularDataDownloadsAlert.swift
├── UIViewController+InternetRequiredAlert.swift
├── UIViewController+ShowToast.swift
├── URL+Converters.swift
├── URL+RemoteSize.swift
├── URL+getQueryParamValue.swift
├── Utilities.swift
├── WebKitViewController.swift
├── podverse-Bridging-Header.h
└── podverse.xcdatamodeld
│ └── podverse.xcdatamodel
│ └── contents
├── PodverseTests
├── Info.plist
└── PodverseTests.swift
├── PodverseUITests
├── Info.plist
└── PodverseUITests.swift
└── README.md
/.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 | .DS_Store
20 |
21 | ## Other
22 | *.moved-aside
23 | *.xcuserstate
24 |
25 | ## Other - mitch uses this directory for local archives
26 | test_builds/
27 |
28 | ## Obj-C/Swift specific
29 | *.hmap
30 | *.dSYM.zip
31 | *.dSYM
32 | *.xcworkspace
33 | .build/
34 | Auth0.plist
35 |
36 | # CocoaPods
37 | Pods/
38 | Podfile.lock
39 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '11'
3 | source 'https://github.com/CocoaPods/Specs.git'
4 |
5 | target 'Podverse' do
6 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
7 | use_frameworks!
8 |
9 | # Pods for Podverse
10 | pod 'Alamofire', '~> 4.0'
11 | pod 'Lock', '~> 2.3'
12 | pod 'Auth0', '~> 1.6'
13 | pod 'ReachabilitySwift', '~> 3'
14 | pod 'p2.OAuth2', '~> 4.0.0'
15 | pod 'StreamingKit', :git => 'https://github.com/podverse/StreamingKit.git', :branch => 'master'
16 | pod 'SDWebImage', '~> 4.0'
17 | pod 'FeedKit', '~> 6.0'
18 | pod 'TaskQueue'
19 | pod 'Fabric'
20 | pod 'Crashlytics'
21 |
22 | target 'PodverseTests' do
23 | inherit! :search_paths
24 | # Pods for testing
25 | end
26 |
27 | target 'PodverseUITests' do
28 | inherit! :search_paths
29 | # Pods for testing
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/Podverse/AboutPlayingItemViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AboutPlayingItemViewController.swift
3 | // Podverse
4 | //
5 | // Created by Creon Creonopoulos on 7/14/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AboutPlayingItemViewController: UIViewController, UIWebViewDelegate {
12 |
13 | let pvMediaPlayer = PVMediaPlayer.shared
14 | let playerHistoryManager = PlayerHistory.manager
15 |
16 | @IBOutlet weak var webView: UIWebView!
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | addObservers()
22 |
23 | loadWebView()
24 |
25 | self.view.backgroundColor = UIColor.black
26 | self.webView.delegate = self
27 | }
28 |
29 | override func viewWillLayoutSubviews() {
30 | super.viewWillLayoutSubviews()
31 | self.webView.scrollView.contentInset = UIEdgeInsets.zero;
32 | }
33 |
34 | deinit {
35 | removeObservers()
36 | }
37 |
38 | fileprivate func addObservers() {
39 | NotificationCenter.default.addObserver(self, selector: #selector(loadWebView), name: .hideClipData, object: nil)
40 | NotificationCenter.default.addObserver(self, selector: #selector(loadWebView), name: .clipUpdated, object: nil)
41 | NotificationCenter.default.addObserver(self, selector: #selector(loadWebView), name: .clipDeleted, object: nil)
42 | }
43 |
44 | fileprivate func removeObservers() {
45 | NotificationCenter.default.removeObserver(self, name: .hideClipData, object: nil)
46 | NotificationCenter.default.removeObserver(self, name: .clipUpdated, object: nil)
47 | NotificationCenter.default.removeObserver(self, name: .clipDeleted, object: nil)
48 | }
49 |
50 | @objc fileprivate func loadWebView() {
51 |
52 | if let item = pvMediaPlayer.nowPlayingItem {
53 | var text = ""
54 |
55 | if item.isClip() {
56 | if let title = item.clipTitle, !title.isEmpty {
57 | text += "" + title + "" + "
"
58 | } else {
59 | text += "Untitled clip" + "
"
60 | }
61 |
62 | if let time = item.readableStartAndEndTime() {
63 | text += "" + time + ""
64 | }
65 |
66 | if let userId = UserDefaults.standard.string(forKey: "userId"), userId == item.ownerId {
67 | text += "Edit Clip"
68 | }
69 |
70 | text += "
"
71 | }
72 |
73 | if let summary = item.episodeSummary, summary.trimmingCharacters(in: .whitespacesAndNewlines).count >= 1, let enrichedSummary = summary.convertPlaybackTimesToUrlSchemeElements() {
74 | text += enrichedSummary
75 | } else {
76 | text += kNoShowNotesMessage
77 | }
78 |
79 | self.webView.loadHTMLString(text.formatHtmlString(isWhiteBg: false), baseURL: nil)
80 |
81 | }
82 |
83 | }
84 |
85 | func showEditClip() {
86 | if !self.pvMediaPlayer.isDataAvailable {
87 | return
88 | }
89 |
90 | if !checkForConnectivity() {
91 | self.showInternetNeededAlertWithDescription(message: "You must be connected to the internet to edit clips.")
92 | return
93 | }
94 |
95 | if let item = self.playerHistoryManager.historyItems.first {
96 | self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"Player", style:.plain, target:nil, action:nil)
97 | self.performSegue(withIdentifier: "Show Make Clip Time", sender: item)
98 | }
99 | }
100 |
101 | func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
102 | if navigationType == UIWebViewNavigationType.linkClicked {
103 | if let url = request.url, url.scheme == "podverse", let query = url.query {
104 | if query == "editClip" {
105 | DispatchQueue.main.async {
106 | self.showEditClip()
107 | }
108 | } else if query == "restartClip" {
109 | if let startTime = self.pvMediaPlayer.nowPlayingItem?.startTime {
110 | self.pvMediaPlayer.seek(toTime: Double(startTime))
111 | }
112 | } else {
113 | let playbackTime = query.mediaPlayerTimeToSeconds()
114 | self.pvMediaPlayer.seek(toTime: Double(playbackTime))
115 | }
116 | } else if let url = request.url {
117 | UIApplication.shared.open(url)
118 | }
119 | return false
120 | }
121 | return true
122 | }
123 |
124 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
125 | if segue.identifier == "Show Make Clip Time" {
126 | if let sender = sender as? PlayerHistoryItem, let makeClipTimeViewController = segue.destination as? MakeClipTimeViewController {
127 | makeClipTimeViewController.editingItem = sender
128 | }
129 | }
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/Podverse/AboutViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AboutViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/3/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import WebKit
11 |
12 | class AboutViewController: PVViewController {
13 |
14 | @IBOutlet weak var loadingIndicator: UIActivityIndicatorView!
15 | @IBOutlet weak var webview: WKWebView!
16 | var requestUrl:URL?
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | self.webview.navigationDelegate = self
21 | if let url = self.requestUrl {
22 | self.webview.load(URLRequest(url: url))
23 | self.webview.allowsBackForwardNavigationGestures = false
24 | }
25 | }
26 |
27 | override func viewWillAppear(_ animated: Bool) {
28 | self.tabBarController?.hidePlayerView()
29 | }
30 |
31 | override func viewWillDisappear(_ animated: Bool) {
32 | self.toggleNowPlayingBar()
33 | }
34 | }
35 |
36 | extension AboutViewController:WKNavigationDelegate {
37 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
38 | self.loadingIndicator.stopAnimating()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Podverse/Array+rearrange.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+rearrange.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/2/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Array { // thanks Leo Dabus https://stackoverflow.com/a/36542411/2608858
12 | mutating func rearrange(from: Int, to: Int) {
13 | if (from == to) { return }
14 | precondition(indices.contains(from) && indices.contains(to), "invalid indexes")
15 | insert(remove(at: from), at: to)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/120-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/120-1.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/Podverse/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 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "29.png",
17 | "scale" : "1x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "58.png",
23 | "scale" : "2x"
24 | },
25 | {
26 | "size" : "29x29",
27 | "idiom" : "iphone",
28 | "filename" : "87.png",
29 | "scale" : "3x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "80.png",
35 | "scale" : "2x"
36 | },
37 | {
38 | "size" : "40x40",
39 | "idiom" : "iphone",
40 | "filename" : "120-1.png",
41 | "scale" : "3x"
42 | },
43 | {
44 | "size" : "57x57",
45 | "idiom" : "iphone",
46 | "filename" : "57.png",
47 | "scale" : "1x"
48 | },
49 | {
50 | "size" : "57x57",
51 | "idiom" : "iphone",
52 | "filename" : "114.png",
53 | "scale" : "2x"
54 | },
55 | {
56 | "size" : "60x60",
57 | "idiom" : "iphone",
58 | "filename" : "120.png",
59 | "scale" : "2x"
60 | },
61 | {
62 | "size" : "60x60",
63 | "idiom" : "iphone",
64 | "filename" : "180.png",
65 | "scale" : "3x"
66 | },
67 | {
68 | "size" : "1024x1024",
69 | "idiom" : "ios-marketing",
70 | "filename" : "icon-1024x1024.png",
71 | "scale" : "1x"
72 | }
73 | ],
74 | "info" : {
75 | "version" : 1,
76 | "author" : "xcode"
77 | }
78 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/AppIcon.appiconset/icon-1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/AppIcon.appiconset/icon-1024x1024.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Blank52.imageset/Blank52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/Blank52.imageset/Blank52.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Blank52.imageset/Blank52@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/Blank52.imageset/Blank52@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Blank52.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Blank52.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Blank52@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/1125x2436.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/1125x2436.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/1242x2208.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/1242x2208.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/2208x1242.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/2208x1242.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/320x480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/320x480.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/640x1136-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/640x1136-1.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/640x1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/640x1136.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/640x960-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/640x960-1.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/640x960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/640x960.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/750x1334.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/LaunchImage.launchimage/750x1334.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "2436h",
7 | "filename" : "1125x2436.png",
8 | "minimum-system-version" : "11.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "extent" : "full-screen",
14 | "idiom" : "iphone",
15 | "subtype" : "736h",
16 | "filename" : "1242x2208.png",
17 | "minimum-system-version" : "8.0",
18 | "orientation" : "portrait",
19 | "scale" : "3x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "736h",
25 | "filename" : "2208x1242.png",
26 | "minimum-system-version" : "8.0",
27 | "orientation" : "landscape",
28 | "scale" : "3x"
29 | },
30 | {
31 | "extent" : "full-screen",
32 | "idiom" : "iphone",
33 | "subtype" : "667h",
34 | "filename" : "750x1334.png",
35 | "minimum-system-version" : "8.0",
36 | "orientation" : "portrait",
37 | "scale" : "2x"
38 | },
39 | {
40 | "orientation" : "portrait",
41 | "idiom" : "iphone",
42 | "filename" : "640x960.png",
43 | "extent" : "full-screen",
44 | "minimum-system-version" : "7.0",
45 | "scale" : "2x"
46 | },
47 | {
48 | "extent" : "full-screen",
49 | "idiom" : "iphone",
50 | "subtype" : "retina4",
51 | "filename" : "640x1136.png",
52 | "minimum-system-version" : "7.0",
53 | "orientation" : "portrait",
54 | "scale" : "2x"
55 | },
56 | {
57 | "orientation" : "portrait",
58 | "idiom" : "iphone",
59 | "filename" : "320x480.png",
60 | "extent" : "full-screen",
61 | "scale" : "1x"
62 | },
63 | {
64 | "orientation" : "portrait",
65 | "idiom" : "iphone",
66 | "filename" : "640x960-1.png",
67 | "extent" : "full-screen",
68 | "scale" : "2x"
69 | },
70 | {
71 | "orientation" : "portrait",
72 | "idiom" : "iphone",
73 | "filename" : "640x1136-1.png",
74 | "extent" : "full-screen",
75 | "subtype" : "retina4",
76 | "scale" : "2x"
77 | }
78 | ],
79 | "info" : {
80 | "version" : 1,
81 | "author" : "xcode"
82 | }
83 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/PodverseIcon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "PodverseIcon.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "PodverseIcon@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "PodverseIcon@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/PodverseIcon.imageset/PodverseIcon@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/SliderClipPosition.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "slider-clip-position.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/SliderClipPosition.imageset/slider-clip-position.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/SliderClipPosition.imageset/slider-clip-position.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/SliderCurrentPosition.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "slider-current-position.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/SliderCurrentPosition.imageset/slider-current-position.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/SliderCurrentPosition.imageset/slider-current-position.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Tab-Downloads.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "tab-downloads.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "tab-downloads@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "tab-downloads@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Tab-Find.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "tab-find.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "tab-find@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "tab-find@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/Tab-Podcasts.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "tab-podcasts.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "tab-podcasts@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "tab-podcasts@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/add.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "add.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "add@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "add@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/add.imageset/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/add.imageset/add.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/add.imageset/add@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/add.imageset/add@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/add.imageset/add@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/add.imageset/add@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/auto-dl.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "auto-dl.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "auto-dl@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "auto-dl@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/auto-dl.imageset/auto-dl@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/back15.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "back15-48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "back15-72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "back15-96px.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/back15.imageset/back15-48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/back15.imageset/back15-48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/back15.imageset/back15-72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/back15.imageset/back15-72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/back15.imageset/back15-96px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/back15.imageset/back15-96px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner-auth0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "banner-auth0.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner-auth0.imageset/banner-auth0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/banner-auth0.imageset/banner-auth0.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "banner.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "banner@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "banner@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner.imageset/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/banner.imageset/banner.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner.imageset/banner@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/banner.imageset/banner@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/banner.imageset/banner@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/banner.imageset/banner@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clear.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "clear.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "clear@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "clear@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clear.imageset/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clear.imageset/clear.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clear.imageset/clear@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clear.imageset/clear@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clear.imageset/clear@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clear.imageset/clear@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "clip.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "clip@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "clip@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clip.imageset/clip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clip.imageset/clip.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clip.imageset/clip@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clip.imageset/clip@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/clip.imageset/clip@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/clip.imageset/clip@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/cloud.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cloud.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "cloud@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "cloud@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/cloud.imageset/cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/cloud.imageset/cloud.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/cloud.imageset/cloud@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/cloud.imageset/cloud@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/cloud.imageset/cloud@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/cloud.imageset/cloud@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-1.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-1.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-1.dataset/animation-1.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-1.dataset/animation-1.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-10.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-10.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-10.dataset/animation-10.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-10.dataset/animation-10.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-11.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-11.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-11.dataset/animation-11.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-11.dataset/animation-11.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-12.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-12.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-12.dataset/animation-12.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-12.dataset/animation-12.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-13.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-13.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-13.dataset/animation-13.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-13.dataset/animation-13.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-14.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-14.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-14.dataset/animation-14.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-14.dataset/animation-14.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-15.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-15.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-15.dataset/animation-15.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-15.dataset/animation-15.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-16.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-16.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-16.dataset/animation-16.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-16.dataset/animation-16.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-17.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-17.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-17.dataset/animation-17.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-17.dataset/animation-17.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-18.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-18.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-18.dataset/animation-18.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-18.dataset/animation-18.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-19.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-19.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-19.dataset/animation-19.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-19.dataset/animation-19.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-2.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-2.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-2.dataset/animation-2.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-2.dataset/animation-2.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-20.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-20.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-20.dataset/animation-20.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-20.dataset/animation-20.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-3.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-3.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-3.dataset/animation-3.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-3.dataset/animation-3.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-4.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-4.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-4.dataset/animation-4.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-4.dataset/animation-4.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-5.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-5.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-5.dataset/animation-5.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-5.dataset/animation-5.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-6.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-6.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-6.dataset/animation-6.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-6.dataset/animation-6.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-7.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-7.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-7.dataset/animation-7.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-7.dataset/animation-7.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-8.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-8.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-8.dataset/animation-8.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-8.dataset/animation-8.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-9.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "animation-9.tiff"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/drag/animation-9.dataset/animation-9.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/drag/animation-9.dataset/animation-9.tiff
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/forward15.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "forward15-48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "forward15-72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "forward15-96px.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/forward15.imageset/forward15-48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/forward15.imageset/forward15-48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/forward15.imageset/forward15-72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/forward15.imageset/forward15-72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/forward15.imageset/forward15-96px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/forward15.imageset/forward15-96px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/icon_grab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "icon_grab@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "icon_grab@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/icon_grab.imageset/icon_grab@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/icon_grab.imageset/icon_grab@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/icon_grab.imageset/icon_grab@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/icon_grab.imageset/icon_grab@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/pause.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "pause.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "pause@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "pause@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/pause.imageset/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/pause.imageset/pause.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/pause.imageset/pause@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/pause.imageset/pause@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/pause.imageset/pause@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/pause.imageset/pause@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/play.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "play.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "play@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "play@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/play.imageset/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/play.imageset/play.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/play.imageset/play@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/play.imageset/play@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/play.imageset/play@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/play.imageset/play@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/playerror.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "playerror.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "playerror@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "playerror@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/playerror.imageset/playerror.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/playerror.imageset/playerror.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/playerror.imageset/playerror@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/playerror.imageset/playerror@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/playerror.imageset/playerror@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/playerror.imageset/playerror@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipbackward.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "skipbackward.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "skipbackward@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "skipbackward@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipbackward.imageset/skipbackward@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipforward.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "skipforward.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "skipforward@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "skipforward@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipforward.imageset/skipforward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipforward.imageset/skipforward.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipforward.imageset/skipforward@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipforward.imageset/skipforward@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/skipforward.imageset/skipforward@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/skipforward.imageset/skipforward@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed05x.imageset/0_5x_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed05x.imageset/0_5x_48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed05x.imageset/0_5x_72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed05x.imageset/0_5x_72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed05x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "0_5x_48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "0_5x_72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed075x.imageset/0_75x_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed075x.imageset/0_75x_48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed075x.imageset/0_75x_72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed075x.imageset/0_75x_72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed075x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "0_75x_48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "0_75x_72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed125x.imageset/1_25x_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed125x.imageset/1_25x_48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed125x.imageset/1_25x_72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed125x.imageset/1_25x_72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed125x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "1_25x_48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "1_25x_72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed15x.imageset/1_5x_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed15x.imageset/1_5x_48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed15x.imageset/1_5x_72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed15x.imageset/1_5x_72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed15x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "1_5x_48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "1_5x_72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed1x.imageset/1x-72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed1x.imageset/1x-72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed1x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1x-72px.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed2x.imageset/2x_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed2x.imageset/2x_48px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed2x.imageset/2x_72px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/speed2x.imageset/2x_72px.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/speed2x.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "2x_48px.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "2x_72px.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-clips.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "tab-clips.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "tab-clips@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "tab-clips@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-clips.imageset/tab-clips@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-downloads.imageset/tab-downloads@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-find.imageset/tab-find.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-find.imageset/tab-find.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-find.imageset/tab-find@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-find.imageset/tab-find@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-find.imageset/tab-find@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-find.imageset/tab-find@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-more.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "tab-more.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "tab-more@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "tab-more@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-more.imageset/tab-more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-more.imageset/tab-more.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-more.imageset/tab-more@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-more.imageset/tab-more@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-more.imageset/tab-more@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-more.imageset/tab-more@3x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts@2x.png
--------------------------------------------------------------------------------
/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Assets.xcassets/tab-podcasts.imageset/tab-podcasts@3x.png
--------------------------------------------------------------------------------
/Podverse/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 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Podverse/ClipEpisodeTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClipEpisodeTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 9/12/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ClipEpisodeTableViewCell: UITableViewCell {
13 |
14 | @IBOutlet weak var clipTitle: UILabel!
15 | @IBOutlet weak var time: UILabel!
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Podverse/ClipPodcastTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClipPodcastTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 9/12/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | class ClipPodcastTableViewCell: UITableViewCell {
13 |
14 | @IBOutlet weak var clipTitle: UILabel!
15 | @IBOutlet weak var episodePubDate: UILabel!
16 | @IBOutlet weak var episodeTitle: UILabel!
17 | @IBOutlet weak var time: UILabel!
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Podverse/ClipTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClipTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 6/7/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ClipTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var clipTitle: UILabel!
14 | @IBOutlet weak var episodePubDate: UILabel!
15 | @IBOutlet weak var episodeTitle: UILabel!
16 | @IBOutlet weak var podcastImage: UIImageView!
17 | @IBOutlet weak var podcastTitle: UILabel!
18 | @IBOutlet weak var time: UILabel!
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Podverse/Data+ResizeImageData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+ResizeImageData.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/24/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension Data {
13 | func resizeImageData() -> Data? {
14 | guard let image = UIImage(data: self) else {
15 | return nil
16 | }
17 |
18 | var actualHeight: CGFloat = image.size.height
19 | var actualWidth: CGFloat = image.size.width
20 | let maxHeight: CGFloat = 400.0
21 | let maxWidth: CGFloat = 400.0
22 | var imgRatio: CGFloat = actualWidth / actualHeight
23 | let maxRatio: CGFloat = maxWidth / maxHeight
24 | //Half the compression
25 | let compressionQuality: CGFloat = 0.5
26 | if actualHeight > maxHeight || actualWidth > maxWidth {
27 | if imgRatio < maxRatio {
28 | //adjust width according to maxHeight
29 | imgRatio = maxHeight / actualHeight
30 | actualWidth = imgRatio * actualWidth
31 | actualHeight = maxHeight
32 | }
33 | else if imgRatio > maxRatio {
34 | //adjust height according to maxWidth
35 | imgRatio = maxWidth / actualWidth
36 | actualHeight = imgRatio * actualHeight
37 | actualWidth = maxWidth
38 | }
39 | else {
40 | actualHeight = maxHeight
41 | actualWidth = maxWidth
42 | }
43 | }
44 | let rect: CGRect = CGRect(x:0.0, y:0.0, width:actualWidth, height:actualHeight)
45 | UIGraphicsBeginImageContext(rect.size)
46 |
47 | defer {
48 | UIGraphicsEndImageContext()
49 | }
50 |
51 | image.draw(in: rect)
52 | if let img: UIImage = UIGraphicsGetImageFromCurrentImageContext(), let data = UIImageJPEGRepresentation(img, compressionQuality) {
53 | return data
54 | }
55 | else {
56 | return nil
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Podverse/Date+Formatters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+Formatters.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/29/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Date {
12 | func toShortFormatString() -> String {
13 | let formatter = DateFormatter()
14 | formatter.dateStyle = .short
15 | return formatter.string(from: self)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Podverse/DeletingPodcasts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeletingPodcasts.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 11/19/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | final class DeletingPodcasts {
12 | static let shared = DeletingPodcasts()
13 | var podcastKeys = [String]()
14 |
15 | func addPodcast(podcastId:String?, feedUrl:String?) {
16 | if let podcastId = podcastId {
17 | if self.podcastKeys.filter({$0 == podcastId}).count < 1 {
18 | self.podcastKeys.append(podcastId)
19 | }
20 | } else if let feedUrl = feedUrl {
21 | if self.podcastKeys.filter({$0 == feedUrl}).count < 1 {
22 | self.podcastKeys.append(feedUrl)
23 | }
24 | }
25 | }
26 |
27 | func removePodcast(podcastId:String?, feedUrl:String?) {
28 | if let podcastId = podcastId, let index = self.podcastKeys.index(of: podcastId) {
29 | self.podcastKeys.remove(at: index)
30 | } else if let feedUrl = feedUrl, let index = self.podcastKeys.index(of: feedUrl) {
31 | self.podcastKeys.remove(at: index)
32 | }
33 | }
34 |
35 | func hasMatchingId(podcastId:String) -> Bool {
36 | if let _ = self.podcastKeys.index(of: podcastId) {
37 | return true
38 | }
39 |
40 | return false
41 | }
42 |
43 | func hasMatchingUrl(feedUrl:String) -> Bool {
44 | if let _ = self.podcastKeys.index(of: feedUrl) {
45 | return true
46 | }
47 |
48 | return false
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Podverse/DownloadTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownloadTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/4/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DownloadTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var podcastImage: UIImageView!
14 | @IBOutlet weak var episodeTitle: UILabel!
15 | @IBOutlet weak var podcastTitle: UILabel!
16 | @IBOutlet weak var progress: UIProgressView!
17 | @IBOutlet weak var progressStats: UILabel!
18 | @IBOutlet weak var status: UILabel!
19 |
20 | override func awakeFromNib() {
21 | super.awakeFromNib()
22 | // Initialization code
23 | }
24 |
25 | override func setSelected(_ selected: Bool, animated: Bool) {
26 | super.setSelected(selected, animated: animated)
27 |
28 | // Configure the view for the selected state
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Podverse/DownloadingEpisode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownloadingEpisode.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/24/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreData
11 |
12 | final class DownloadingEpisode:Equatable {
13 | var downloadComplete: Bool = false
14 | var mediaUrl: String?
15 | var podcastFeedUrl:String?
16 | var podcastImageUrl: String?
17 | var podcastTitle:String?
18 | var taskIdentifier:Int?
19 | var taskResumeData:Data?
20 | var title:String?
21 | var totalBytesWritten:Float?
22 | var totalBytesExpectedToWrite:Float?
23 |
24 | var formattedTotalBytesDownloaded: String {
25 | get {
26 | if let currentBytes = totalBytesWritten, let totalBytes = totalBytesExpectedToWrite {
27 | // Format the total bytes into a human readable KB or MB number
28 | let dataFormatter = ByteCountFormatter()
29 |
30 | let formattedCurrentBytesDownloaded = dataFormatter.string(fromByteCount: Int64(currentBytes))
31 | let formattedTotalFileBytes = dataFormatter.string(fromByteCount: Int64(totalBytes))
32 |
33 | if progress == 1.0 {
34 | return formattedTotalFileBytes
35 | } else {
36 | return "\(formattedCurrentBytesDownloaded) / \(formattedTotalFileBytes)"
37 | }
38 | } else {
39 | return ""
40 | }
41 | }
42 | }
43 |
44 | var progress: Float {
45 | get {
46 | if let currentBytes = totalBytesWritten, let totalBytes = totalBytesExpectedToWrite {
47 | return currentBytes / totalBytes
48 | } else {
49 | return Float(0)
50 | }
51 | }
52 | }
53 |
54 | init(episode:Episode) {
55 | downloadComplete = false
56 | mediaUrl = episode.mediaUrl
57 | podcastFeedUrl = episode.podcast.feedUrl
58 | podcastImageUrl = episode.podcast.imageUrl
59 | podcastTitle = episode.podcast.title
60 | taskIdentifier = nil
61 | taskResumeData = nil
62 | title = episode.title
63 | totalBytesWritten = nil
64 | totalBytesExpectedToWrite = nil
65 |
66 | addToDownloadHistory()
67 | }
68 |
69 | func addToDownloadHistory() {
70 | if var downloadingMediaUrls = UserDefaults.standard.array(forKey: kDownloadingMediaUrls) as? [String], let mediaUrl = self.mediaUrl {
71 | if !downloadingMediaUrls.contains(mediaUrl) {
72 | downloadingMediaUrls.append(mediaUrl)
73 | UserDefaults.standard.setValue(downloadingMediaUrls, forKey: kDownloadingMediaUrls)
74 | }
75 | } else if let mediaUrl = self.mediaUrl {
76 | UserDefaults.standard.setValue([mediaUrl], forKey: kDownloadingMediaUrls)
77 | }
78 |
79 | }
80 |
81 | func removeFromDownloadHistory() {
82 | if let downloadingMediaUrls = UserDefaults.standard.array(forKey: kDownloadingMediaUrls) as? [String] {
83 | let results = downloadingMediaUrls.filter { $0 != mediaUrl }
84 | UserDefaults.standard.setValue(results, forKey: kDownloadingMediaUrls)
85 | }
86 | }
87 |
88 | }
89 |
90 | func == (lhs: DownloadingEpisode, rhs: DownloadingEpisode) -> Bool {
91 | return lhs.mediaUrl == rhs.mediaUrl
92 | }
93 |
--------------------------------------------------------------------------------
/Podverse/DownloadingEpisodeList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownloadingEpisodeList.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/24/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class DownloadingEpisodeList {
12 | static var shared = DownloadingEpisodeList()
13 |
14 | var downloadingEpisodes = [DownloadingEpisode]()
15 |
16 | static func removeDownloadingEpisodeWithMediaURL(mediaUrl:String?) {
17 | var downloadingEpisodes = DownloadingEpisodeList.shared.downloadingEpisodes
18 |
19 | if let mediaUrl = mediaUrl, let index = downloadingEpisodes.index(where: { $0.mediaUrl == mediaUrl }), index < downloadingEpisodes.count {
20 | downloadingEpisodes[index].removeFromDownloadHistory()
21 | downloadingEpisodes.remove(at: index)
22 | PVDownloader.shared.decrementBadge()
23 | DownloadingEpisodeList.shared.downloadingEpisodes = downloadingEpisodes
24 | }
25 | }
26 |
27 | static func removeAllEpisodesForPodcast(feedUrl: String) {
28 | DownloadingEpisodeList.shared.downloadingEpisodes = DownloadingEpisodeList.shared.downloadingEpisodes.filter({$0.podcastFeedUrl != feedUrl})
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Podverse/Episode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Episode.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/24/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreData
11 |
12 | class Episode: NSManagedObject {
13 | @NSManaged var duration: NSNumber?
14 | @NSManaged var fileName: String?
15 | @NSManaged var guid: String?
16 | @NSManaged var link: String?
17 | @NSManaged var mediaBytes: NSNumber?
18 | @NSManaged var mediaType: String?
19 | @NSManaged var mediaUrl: String?
20 | @NSManaged var pubDate: Date?
21 | @NSManaged var summary: String?
22 | @NSManaged var title: String?
23 | @NSManaged var uuid: String?
24 | @NSManaged var podcast: Podcast
25 |
26 | static func episodeForMediaUrl(mediaUrlString: String, managedObjectContext:NSManagedObjectContext? = nil) -> Episode? {
27 | let moc = managedObjectContext ?? CoreDataHelper.createMOCForThread(threadType: .mainThread)
28 |
29 | let predicate = NSPredicate(format: "mediaUrl == %@", mediaUrlString)
30 | let episodeSet = CoreDataHelper.fetchEntities(className: "Episode", predicate: predicate, moc:moc) as? [Episode]
31 |
32 | return episodeSet?.first
33 | }
34 |
35 | static func jsonToPlayerHistoryItem(json: [String:Any]) -> PlayerHistoryItem? {
36 |
37 | if let podcast = json["podcast"] as? [String:Any], let isPublic = json["isPublic"] as? Bool {
38 | let podcastId = podcast["id"] as? String
39 | let podcastTitle = podcast["title"] as? String
40 | let podcastImageUrl = podcast["imageUrl"] as? String
41 |
42 | let episodeId = json["id"] as? String
43 | let episodeDuration = json["duration"] as? Int64
44 | let episodeMediaUrl = json["mediaUrl"] as? String
45 | let episodeTitle = json["title"] as? String
46 | let episodeImageUrl = json["imageUrl"] as? String
47 | let episodeSummary = json["summary"] as? String
48 | let episodePubDate = (json["pubDate"] as? String)?.toServerDate()
49 | let episodeLastUpdated = (json["lastUpdated"] as? String)?.toServerDate()
50 |
51 | let item = PlayerHistoryItem(mediaRefId: nil, podcastId: podcastId, podcastFeedUrl: nil, podcastTitle: podcastTitle, podcastImageUrl: podcastImageUrl, episodeDuration: episodeDuration, episodeId: episodeId, episodeMediaUrl: episodeMediaUrl, episodeTitle: episodeTitle, episodeImageUrl: episodeImageUrl, episodeSummary: episodeSummary, episodePubDate: episodePubDate, startTime: nil, endTime: nil, clipTitle: nil, ownerName: nil, ownerId: nil, hasReachedEnd: false, lastPlaybackPosition: nil, lastUpdated: episodeLastUpdated, isPublic: isPublic)
52 |
53 | return item
54 | }
55 |
56 | return nil
57 | }
58 |
59 | static func retrieveEpisodeFromServer(id:String, completion: @escaping (_ playerHistoryItem:PlayerHistoryItem?) -> Void) {
60 | if let url = URL(string: BASE_URL + "api/episodes") {
61 |
62 | let request = NSMutableURLRequest(url: url, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30)
63 |
64 | request.setValue("application/json", forHTTPHeaderField: "Content-Type")
65 |
66 | request.httpMethod = "POST"
67 |
68 | var values: [String: Any] = [:]
69 | values["id"] = id
70 |
71 | do {
72 | request.httpBody = try JSONSerialization.data(withJSONObject: values, options: [])
73 | } catch {
74 | print("Error: \(error.localizedDescription)")
75 | }
76 |
77 | let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
78 |
79 | hideNetworkActivityIndicator()
80 | guard error == nil else {
81 | print("Error: \(error?.localizedDescription ?? "Unknown Error")")
82 | DispatchQueue.main.async {
83 | completion(nil)
84 | }
85 | return
86 | }
87 |
88 | if let data = data {
89 | do {
90 | if let responseJSON = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
91 | let item = jsonToPlayerHistoryItem(json: responseJSON)
92 | DispatchQueue.main.async {
93 | completion(item)
94 | }
95 | }
96 | } catch {
97 | print("Error: " + error.localizedDescription)
98 | }
99 | }
100 | }
101 |
102 | task.resume()
103 |
104 | }
105 | }
106 |
107 | // This is a hacky method for determining what the Podverse episode ID is for an episode that the user has parsed on their phone locally.
108 | // In the future, we could avoid this by parsing all feeds on our server instead of on users' devices.
109 | static func retrieveEpisodeIdFromServer(mediaUrl:String, completion: @escaping (_ episodeId:String?) -> Void) {
110 | if let url = URL(string: BASE_URL + "api/episodes/id") {
111 |
112 | let request = NSMutableURLRequest(url: url, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30)
113 |
114 | request.setValue("application/json", forHTTPHeaderField: "Content-Type")
115 | request.httpMethod = "POST"
116 |
117 | var values: [String:Any] = [:]
118 | values["mediaUrl"] = mediaUrl
119 |
120 | do {
121 | request.httpBody = try JSONSerialization.data(withJSONObject: values, options: [])
122 | } catch {
123 | print(error)
124 | DispatchQueue.main.async {
125 | completion(nil)
126 | }
127 | return
128 | }
129 |
130 | let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
131 |
132 | hideNetworkActivityIndicator()
133 | guard error == nil else {
134 | print("Error: \(error?.localizedDescription ?? "Unknown Error")")
135 | DispatchQueue.main.async {
136 | completion(nil)
137 | }
138 | return
139 | }
140 |
141 | if let data = data {
142 | do {
143 | if let responseJSON = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
144 | if let id = responseJSON["id"] as? String {
145 | DispatchQueue.main.async {
146 | completion(id)
147 | }
148 | } else {
149 | DispatchQueue.main.async {
150 | completion(nil)
151 | }
152 | }
153 | }
154 | } catch {
155 | print("Error: " + error.localizedDescription)
156 | }
157 | }
158 | }
159 |
160 | task.resume()
161 |
162 | }
163 | }
164 |
165 | static let episodeKey = "episode"
166 |
167 | }
168 |
169 |
170 |
--------------------------------------------------------------------------------
/Podverse/EpisodeTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EpisodeTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 5/6/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class EpisodeTableViewCell: UITableViewCell {
12 | @IBOutlet weak var title: UILabel!
13 | @IBOutlet weak var summary: UILabel!
14 | @IBOutlet weak var duration: UILabel!
15 | @IBOutlet weak var pubDate: UILabel!
16 | @IBOutlet weak var button: UIButton!
17 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
18 |
19 | override func awakeFromNib() {
20 | // The activityIndicator can be nil in the SearchPodcastViewController
21 | if self.activityIndicator != nil {
22 | self.activityIndicator.hidesWhenStopped = true
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Podverse/FindBrowseGroupsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FindBrowseGroupsViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 10/22/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | //import UIKit
10 | //
11 | //class FindBrowseGroupsViewController: PVViewController {
12 | //
13 | // var categories = [SearchCategory]()
14 | // var networks = [SearchNetwork]()
15 | //
16 | // var categoryParentId:Int64?
17 | // var shouldLoadCategories:Bool = false
18 | // var shouldLoadNetworks:Bool = false
19 | //
20 | // @IBOutlet weak var tableView: UITableView!
21 | //
22 | // override func viewDidLoad() {
23 | // super.viewDidLoad()
24 | //
25 | // if self.shouldLoadCategories {
26 | //
27 | // self.title = "Categories"au
28 | //
29 | // SearchCategory.retrieveCategoriesFromServer(parentId: nil, { categoriesArray in
30 | // DispatchQueue.main.async {
31 | // if let categoriesArray = categoriesArray {
32 | // let filteredArray = SearchCategory.filterCategories(categories: categoriesArray, parentId: self.categoryParentId)
33 | // self.categories = filteredArray
34 | // self.tableView.reloadData()
35 | // }
36 | // }
37 | // })
38 | // } else if self.shouldLoadNetworks {
39 | //
40 | // self.title = "Networks"
41 | //
42 | // SearchNetwork.retrieveNetworksFromServer({ networksArray in
43 | // DispatchQueue.main.async {
44 | // if let networksArray = networksArray {
45 | // self.networks = networksArray
46 | // self.tableView.reloadData()
47 | // }
48 | // }
49 | // })
50 | // }
51 | //
52 | // }
53 | //
54 | //}
55 | //
56 | //extension FindBrowseGroupsViewController:UITableViewDelegate, UITableViewDataSource {
57 | //
58 | // func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
59 | // if self.shouldLoadCategories {
60 | // return self.categories.count
61 | // } else {
62 | // return self.networks.count
63 | // }
64 | // }
65 | //
66 | // func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
67 | // let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
68 | //
69 | // if self.shouldLoadCategories {
70 | // let title = self.categories[indexPath.row].name
71 | // cell.textLabel?.text = title
72 | // } else {
73 | // let title = self.networks[indexPath.row].name
74 | // cell.textLabel?.text = title
75 | // }
76 | //
77 | // return cell
78 | // }
79 | //
80 | // func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
81 | // let sender = self.shouldLoadCategories ? "Categories" : "Networks"
82 | // self.performSegue(withIdentifier: "Show Browse Podcasts", sender: sender)
83 | // }
84 | //
85 | // override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
86 | // if segue.identifier == "Show Browse Podcasts", let findBrowsePodcastsVC = segue.destination as? FindBrowsePodcastsViewController, let indexPath = self.tableView.indexPathForSelectedRow {
87 | //
88 | // if let sender = sender as? String, sender == "Categories" {
89 | // if indexPath.row < self.categories.count {
90 | // let category = self.categories[indexPath.row]
91 | // findBrowsePodcastsVC.groupTitle = category.name
92 | // findBrowsePodcastsVC.categoryName = category.name
93 | // }
94 | // } else {
95 | // if indexPath.row < self.networks.count {
96 | // let network = self.networks[indexPath.row]
97 | // if let name = network.name {
98 | // findBrowsePodcastsVC.groupTitle = name
99 | // findBrowsePodcastsVC.networkName = name
100 | // }
101 | // }
102 | // }
103 | //
104 | // }
105 | // }
106 | //
107 | //}
108 |
109 |
--------------------------------------------------------------------------------
/Podverse/FindBrowsePodcastsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FindBrowsePodcastsViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 10/22/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FindBrowsePodcastsViewController: PVViewController {
12 |
13 | var podcasts = [SearchPodcast]()
14 | var groupTitle = ""
15 | var categoryName:String?
16 | var networkName:String?
17 |
18 | @IBOutlet weak var tableView: UITableView!
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | var params = Dictionary()
24 |
25 | if let categoryName = categoryName {
26 | self.title = "Category"
27 | params["filters[categories.name]"] = categoryName
28 | } else if let networkName = networkName {
29 | self.title = "Network"
30 | params["filters[network.name]"] = networkName
31 | }
32 |
33 | params["sort_by"] = "buzz_score"
34 | params["sort_order"] = "desc"
35 |
36 | // SearchClientSwift.search(query: "*", params: params, type: "shows") { (serviceResponse) in
37 | //
38 | // self.podcasts.removeAll()
39 | //
40 | // if let response = serviceResponse.0 {
41 | // // let page = response["page"] as? String
42 | // // let query = response["query"] as? String
43 | // // let results_per_page = response["results_per_page"] as? String
44 | // // let total_results = response["total_results"] as? String
45 | //
46 | // if let results = response["results"] as? [AnyObject] {
47 | // for result in results {
48 | // if let searchResult = SearchPodcast.convertJSONToSearchPodcast(result) {
49 | // self.podcasts.append(searchResult)
50 | // }
51 | // }
52 | // }
53 | //
54 | // DispatchQueue.main.async {
55 | // self.tableView.reloadData()
56 | // }
57 | // }
58 | //
59 | // if let error = serviceResponse.1 {
60 | // print(error.localizedDescription)
61 | // }
62 | //
63 | // }
64 |
65 | }
66 |
67 | }
68 |
69 | extension FindBrowsePodcastsViewController: UITableViewDataSource, UITableViewDelegate {
70 |
71 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
72 | return 44
73 | }
74 |
75 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
76 | return self.groupTitle
77 | }
78 |
79 | func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
80 | return 96.5
81 | }
82 |
83 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
84 | return 96.5
85 | }
86 |
87 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
88 | return self.podcasts.count
89 | }
90 |
91 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
92 | let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PodcastSearchResultTableViewCell
93 |
94 | let podcast = self.podcasts[indexPath.row]
95 |
96 | cell.title.text = podcast.title
97 | cell.hosts.text = podcast.hosts
98 | cell.categories.text = podcast.categories
99 |
100 | cell.pvImage.image = Podcast.retrievePodcastImage(podcastImageURLString: podcast.imageUrl, feedURLString: nil, completion: { image in
101 | cell.pvImage.image = image
102 | })
103 |
104 | return cell
105 | }
106 |
107 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
108 | let podcast = self.podcasts[indexPath.row]
109 | SearchPodcast.showSearchPodcastActions(searchPodcast: podcast, vc: self)
110 | }
111 |
112 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
113 |
114 | if segue.identifier == "Show Search Podcast" {
115 | if let searchPodcastVC = segue.destination as? SearchPodcastViewController, let indexPath = self.tableView.indexPathForSelectedRow, indexPath.row < self.podcasts.count {
116 | let podcast = podcasts[indexPath.row]
117 | searchPodcastVC.searchPodcast = podcast
118 | searchPodcastVC.filterTypeOverride = .about
119 | }
120 | }
121 |
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/Podverse/FindSearchTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FindSearchTableViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/8/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FindSearchTableViewController: PVViewController {
12 |
13 | var podcasts = [SearchPodcast]()
14 |
15 | @IBOutlet weak var activityView: UIView!
16 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
17 | @IBOutlet weak var tableView: UITableView!
18 | @IBOutlet weak var searchBar: UISearchBar!
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | self.title = "Search"
24 |
25 | self.searchBar.delegate = self
26 | self.searchBar.returnKeyType = .done
27 |
28 | self.activityIndicator.hidesWhenStopped = true
29 | hideActivityIndicator()
30 |
31 | self.tableView.isHidden = true
32 |
33 | let requestPodcast = UIBarButtonItem(title: "Request", style: .plain, target: self, action: #selector(segueToRequestPodcastForm))
34 | self.navigationItem.rightBarButtonItems = [requestPodcast]
35 |
36 | loadSearchForPodcastsMessage()
37 | }
38 |
39 | @objc func segueToRequestPodcastForm() {
40 | self.tableView.reloadData()
41 | if let webKitVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "WebKitVC") as? WebKitViewController {
42 | webKitVC.urlString = kFormRequestPodcastUrl
43 | self.hideNowPlayingBar()
44 | self.navigationController?.pushViewController(webKitVC, animated: true)
45 | }
46 | }
47 |
48 | func loadNoDataView(message: String, buttonTitle: String?, buttonPressed: Selector?) {
49 |
50 | if let noDataView = self.view.subviews.first(where: { $0.tag == kNoDataViewTag}) {
51 | noDataView.removeFromSuperview()
52 | }
53 |
54 | self.addNoDataViewWithMessage(message, buttonTitle: buttonTitle, buttonImage: nil, retryPressed: buttonPressed)
55 |
56 | showNoDataView()
57 |
58 | }
59 |
60 | func loadNoInternetMessage() {
61 | loadNoDataView(message: Strings.Errors.noClipsInternet, buttonTitle: "Retry", buttonPressed: #selector(ClipsTableViewController.resetAndRetrieveClips))
62 | }
63 |
64 | func loadNoResultsMessage() {
65 | loadNoDataView(message: Strings.Errors.noSearchResultsFound, buttonTitle: "Request a podcast", buttonPressed: #selector(segueToRequestPodcastForm))
66 | }
67 |
68 | func loadSearchForPodcastsMessage() {
69 | loadNoDataView(message: "Search for podcasts by title", buttonTitle: nil, buttonPressed: nil)
70 | }
71 |
72 | func showActivityIndicator() {
73 | self.tableView.isHidden = true
74 | self.activityIndicator.startAnimating()
75 | self.activityView.isHidden = false
76 | }
77 |
78 | func hideActivityIndicator() {
79 | self.activityIndicator.stopAnimating()
80 | self.activityView.isHidden = true
81 | }
82 |
83 | @objc fileprivate func searchPodcasts(_ text:String) {
84 | self.podcasts.removeAll()
85 |
86 | guard checkForConnectivity() else {
87 | loadNoInternetMessage()
88 | return
89 | }
90 |
91 | showActivityIndicator()
92 |
93 | SearchPodcast.searchPodcastsByTitle(title: text) { searchPodcasts in
94 | if let searchPodcasts = searchPodcasts {
95 | self.podcasts = searchPodcasts
96 | }
97 |
98 | DispatchQueue.main.async {
99 | self.hideActivityIndicator()
100 |
101 | if self.podcasts.isEmpty {
102 | self.loadNoResultsMessage()
103 | } else {
104 | self.tableView.reloadData()
105 | self.tableView.isHidden = false
106 | }
107 |
108 | }
109 | }
110 | }
111 |
112 | }
113 |
114 | extension FindSearchTableViewController: UITableViewDataSource, UITableViewDelegate {
115 |
116 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
117 | return 96.5
118 | }
119 |
120 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
121 | return self.podcasts.count
122 | }
123 |
124 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
125 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PodcastSearchResultTableViewCell
126 |
127 | let podcast = self.podcasts[indexPath.row]
128 |
129 | cell.title.text = podcast.title
130 | cell.hosts.text = podcast.hosts
131 | cell.categories.text = podcast.categories
132 |
133 | cell.pvImage.sd_setImage(with: URL(string: podcast.imageUrl ?? ""), placeholderImage: #imageLiteral(resourceName: "PodverseIcon"))
134 |
135 | return cell
136 | }
137 |
138 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
139 | let podcast = self.podcasts[indexPath.row]
140 | SearchPodcast.showSearchPodcastActions(searchPodcast: podcast, vc: self)
141 | }
142 |
143 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
144 | if segue.identifier == "Show Search Podcast" {
145 | if let searchPodcastVC = segue.destination as? SearchPodcastViewController, let indexPath = self.tableView.indexPathForSelectedRow, indexPath.row < self.podcasts.count {
146 | let podcast = podcasts[indexPath.row]
147 | searchPodcastVC.searchPodcast = podcast
148 |
149 | if let sender = sender as? String, sender == "About" {
150 | searchPodcastVC.filterTypeOverride = .about
151 | } else if let sender = sender as? String, sender == "Clips" {
152 | searchPodcastVC.filterTypeOverride = .clips
153 | } else if let sender = sender as? String, sender == "Episodes" {
154 | searchPodcastVC.filterTypeOverride = .episodes
155 | }
156 | }
157 | }
158 | }
159 |
160 | }
161 |
162 | extension FindSearchTableViewController: UISearchBarDelegate {
163 | func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
164 | searchBar.resignFirstResponder()
165 | }
166 |
167 | func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
168 | if let text = searchBar.text, text.count > 2 {
169 | NSObject.cancelPreviousPerformRequests(withTarget: self)
170 | perform(#selector(searchPodcasts(_:)), with: text, afterDelay: 0.4)
171 | }
172 | }
173 |
174 | func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
175 | searchBar.resignFirstResponder()
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/Podverse/FindTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FindTableViewController.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/15/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FindTableViewController: PVViewController {
12 |
13 | @IBOutlet weak var tableView: UITableView!
14 |
15 | let reachability = PVReachability.shared
16 |
17 | let findSearchArray = ["Search", "Add Podcast by RSS"]
18 |
19 | var podcastVC:PodcastsTableViewController? {
20 | get {
21 | if let navController = self.tabBarController?.viewControllers?.first as? UINavigationController, let podcastTable = navController.topViewController as? PodcastsTableViewController {
22 | return podcastTable
23 | }
24 |
25 | return nil
26 | }
27 |
28 | }
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 |
33 | self.title = "Find"
34 | }
35 |
36 | }
37 |
38 | extension FindTableViewController:UITableViewDelegate, UITableViewDataSource {
39 | func numberOfSections(in tableView: UITableView) -> Int {
40 | return 1
41 | }
42 |
43 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
44 | return 44
45 | }
46 |
47 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
48 | return "Podcasts"
49 | }
50 |
51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
52 | return findSearchArray.count
53 | }
54 |
55 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
56 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
57 | let title = findSearchArray[indexPath.row]
58 | cell.textLabel!.text = title
59 | return cell
60 | }
61 |
62 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
63 | if indexPath.row == 0 {
64 | self.performSegue(withIdentifier: "Search for Podcasts", sender: tableView)
65 | }
66 | else {
67 | if !checkForConnectivity() {
68 | showInternetNeededAlertWithDescription(message: "Connect to WiFi or cellular data to add podcast by RSS URL.")
69 | return
70 | }
71 | let addByRSSAlert = UIAlertController(title: "Add Podcast by RSS Feed", message: "Type the RSS feed URL below. NOTE: Creating clips is not supported for podcast's added by RSS Feed.", preferredStyle: UIAlertControllerStyle.alert)
72 |
73 | addByRSSAlert.addTextField(configurationHandler: {(textField: UITextField!) in
74 | textField.placeholder = "https://rssfeed.example.com/"
75 | })
76 |
77 | addByRSSAlert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
78 |
79 | addByRSSAlert.addAction(UIAlertAction(title: "Add", style: .default, handler: { (action: UIAlertAction!) in
80 | if let textField = addByRSSAlert.textFields?[0], let text = textField.text {
81 | DispatchQueue.global().async {
82 | PVSubscriber.subscribeToPodcast(podcastId: nil, feedUrl: text)
83 | }
84 | }
85 | }))
86 |
87 | present(addByRSSAlert, animated: true, completion: nil)
88 |
89 | }
90 |
91 | tableView.deselectRow(at: indexPath, animated: true)
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Podverse/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 |
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.1.0
21 | CFBundleURLTypes
22 |
23 |
24 | CFBundleTypeRole
25 | Editor
26 | CFBundleURLSchemes
27 |
28 | podverse
29 |
30 |
31 |
32 | CFBundleVersion
33 | 8
34 | Fabric
35 |
36 | APIKey
37 | 4de8d263bbd958f598aae11ba2d716f6c29190e9
38 | Kits
39 |
40 |
41 | KitInfo
42 |
43 | KitName
44 | Crashlytics
45 |
46 |
47 |
48 | LSRequiresIPhoneOS
49 |
50 | NSAppTransportSecurity
51 |
52 | NSAllowsArbitraryLoads
53 |
54 |
55 | UIBackgroundModes
56 |
57 | audio
58 |
59 | UIMainStoryboardFile
60 | Main
61 | UIRequiredDeviceCapabilities
62 |
63 | armv7
64 |
65 | UIStatusBarStyle
66 | UIStatusBarStyleLightContent
67 | UIStatusBarTintParameters
68 |
69 | UINavigationBar
70 |
71 | Style
72 | UIBarStyleDefault
73 | Translucent
74 |
75 |
76 |
77 | UISupportedInterfaceOrientations
78 |
79 | UIInterfaceOrientationPortrait
80 |
81 | UIViewControllerBasedStatusBarAppearance
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Podverse/Int64+MediaPlayerString.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int64+MediaPlayerString.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 5/29/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | extension Int64 {
10 | func toMediaPlayerString() -> String {
11 | let hours = self / 3600
12 | let minutes = self / 60 % 60
13 | let seconds = self % 60
14 |
15 | var timeString = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
16 |
17 | if hours < 10 {
18 | timeString = String(format:"%01i:%02i:%02i", hours, minutes, seconds)
19 | }
20 |
21 | if hours == 0 {
22 | if minutes > 9 {
23 | timeString = String(format:"%02i:%02i", minutes, seconds)
24 | } else {
25 | timeString = String(format:"%01i:%02i", minutes, seconds)
26 | }
27 | }
28 |
29 | return timeString
30 | }
31 |
32 | func toDurationString() -> String {
33 | let hours = self / 3600
34 | let minutes = self / 60 % 60
35 | let seconds = self % 60
36 |
37 | var timeString = ""
38 |
39 | if hours > 0 {
40 | timeString += String(hours) + "h "
41 | }
42 |
43 | if minutes > 0 {
44 | timeString += String(minutes) + "m "
45 | }
46 |
47 | if seconds > 0 {
48 | timeString += String(seconds) + "s"
49 | }
50 |
51 | return timeString.trimmingCharacters(in: .whitespaces)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Podverse/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/29/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Lock
11 |
12 | class LoginViewController: PVViewController {
13 |
14 | let pvAuth = PVAuth.shared
15 |
16 | @IBOutlet weak var loginToLabel: UILabel!
17 | @IBOutlet weak var loginToText: UITextView!
18 | @IBOutlet weak var noLoginNeededToLabel: UILabel!
19 | @IBOutlet weak var noLoginNeededToText: UITextView!
20 | @IBOutlet weak var loginButton: UIButton!
21 | @IBOutlet weak var noThanksButton: UIButton!
22 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
23 | @IBOutlet weak var loggingInLabel: UILabel!
24 |
25 | @IBAction func login(_ sender: Any) {
26 | pvAuth.showAuth0Lock(vc: self)
27 | }
28 |
29 | @IBAction func cancel(_ sender: Any) {
30 | dismiss(animated: true, completion: nil)
31 | }
32 |
33 | fileprivate func addObservers() {
34 | NotificationCenter.default.addObserver(self, selector: #selector(self.loggingIn(_:)), name: .loggingIn, object: nil)
35 | NotificationCenter.default.addObserver(self, selector: #selector(self.loggedInSuccessfully(_:)), name: .loggedInSuccessfully, object: nil)
36 | NotificationCenter.default.addObserver(self, selector: #selector(self.loginFailed(_:)), name: .loginFailed, object: nil)
37 | }
38 |
39 | fileprivate func removeObservers() {
40 | NotificationCenter.default.removeObserver(self, name: .loggingIn, object: nil)
41 | NotificationCenter.default.removeObserver(self, name: .loggedInSuccessfully, object: nil)
42 | NotificationCenter.default.removeObserver(self, name: .loginFailed, object: nil)
43 | }
44 |
45 | override func viewDidLoad() {
46 | super.viewDidLoad()
47 | addObservers()
48 | self.activityIndicator.hidesWhenStopped = true
49 | self.loggingInLabel.isHidden = true
50 | }
51 |
52 | deinit {
53 | removeObservers()
54 | }
55 |
56 | }
57 |
58 | extension LoginViewController {
59 | @objc func loggingIn(_ notification:Notification) {
60 | self.loginToLabel.isHidden = true
61 | self.loginToText.isHidden = true
62 | self.noLoginNeededToLabel.isHidden = true
63 | self.noLoginNeededToText.isHidden = true
64 | self.loginButton.isHidden = true
65 | self.noThanksButton.isHidden = true
66 | self.loggingInLabel.isHidden = false
67 | self.activityIndicator.startAnimating()
68 | }
69 |
70 | @objc func loggedInSuccessfully(_ notification:Notification) {
71 | self.activityIndicator.stopAnimating()
72 | self.dismiss(animated: true, completion: nil)
73 | }
74 |
75 | @objc func loginFailed(_ notification:Notification) {
76 | self.loginToLabel.isHidden = false
77 | self.loginToText.isHidden = false
78 | self.noLoginNeededToLabel.isHidden = false
79 | self.noLoginNeededToText.isHidden = false
80 | self.loginButton.isHidden = false
81 | self.noThanksButton.isHidden = false
82 | self.loggingInLabel.isHidden = true
83 | self.activityIndicator.stopAnimating()
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Podverse/MoreTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MoreTableViewController.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/15/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MoreTableViewController: PVViewController {
12 |
13 | let pvAuth = PVAuth.shared
14 |
15 | @IBOutlet weak var tableView: UITableView!
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | self.title = "More"
21 |
22 | addObservers()
23 | }
24 |
25 | deinit {
26 | removeObservers()
27 | }
28 |
29 | fileprivate func addObservers() {
30 | NotificationCenter.default.addObserver(self, selector: #selector(self.loggingIn(_:)), name: .loggingIn, object: nil)
31 | NotificationCenter.default.addObserver(self, selector: #selector(self.loggedInSuccessfully(_:)), name: .loggedInSuccessfully, object: nil)
32 | NotificationCenter.default.addObserver(self, selector: #selector(self.loginFailed(_:)), name: .loginFailed, object: nil)
33 | NotificationCenter.default.addObserver(self, selector: #selector(self.loggedOutSuccessfully(_:)), name: .loggedOutSuccessfully, object: nil)
34 | }
35 |
36 | fileprivate func removeObservers() {
37 | NotificationCenter.default.removeObserver(self, name: .loggingIn, object: nil)
38 | NotificationCenter.default.removeObserver(self, name: .loggedInSuccessfully, object: nil)
39 | NotificationCenter.default.removeObserver(self, name: .loginFailed, object: nil)
40 | NotificationCenter.default.removeObserver(self, name: .loggedOutSuccessfully, object: nil)
41 | }
42 |
43 | }
44 |
45 | extension MoreTableViewController:UITableViewDelegate, UITableViewDataSource {
46 |
47 | func numberOfSections(in tableView: UITableView) -> Int {
48 | return 2
49 | }
50 |
51 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
52 | if section == 0 {
53 | return "Features"
54 | } else {
55 | return "Podverse"
56 | }
57 | }
58 |
59 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
60 | if section == 0 {
61 | return 3
62 | } else {
63 | return 2
64 | }
65 | }
66 |
67 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
68 | let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
69 | let section = indexPath.section
70 | let row = indexPath.row
71 |
72 | if section == 0 {
73 | if row == 0 {
74 | cell.textLabel?.text = "Playlists"
75 | } else if row == 1 {
76 | cell.textLabel?.text = "Settings"
77 | } else if row == 2 {
78 | if let _ = UserDefaults.standard.string(forKey: "idToken") {
79 | cell.textLabel?.text = "Log out"
80 | } else {
81 | cell.textLabel?.text = "Log in"
82 | }
83 | }
84 | } else {
85 | if row == 0 {
86 | cell.textLabel?.text = "Feedback"
87 | } else {
88 | cell.textLabel?.text = "About"
89 | }
90 | }
91 |
92 | return cell
93 | }
94 |
95 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
96 |
97 | let section = indexPath.section
98 | let row = indexPath.row
99 |
100 | if section == 0 {
101 | if row == 0 {
102 | performSegue(withIdentifier: "Show Playlists", sender: nil)
103 | } else if row == 1 {
104 | performSegue(withIdentifier: "Show Settings", sender: nil)
105 | } else if row == 2 {
106 | if let _ = UserDefaults.standard.string(forKey: "idToken") {
107 |
108 | let logoutAlert = UIAlertController(title: "Log out", message: "Are you sure?", preferredStyle: .alert)
109 |
110 | logoutAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action: UIAlertAction!) in
111 | self.pvAuth.removeUserInfo()
112 | tableView.reloadData()
113 | }))
114 |
115 | logoutAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
116 |
117 | present(logoutAlert, animated: true, completion: nil)
118 |
119 | } else {
120 | pvAuth.showAuth0Lock(vc: self)
121 | }
122 | }
123 | } else {
124 | if row == 0 {
125 | if let webKitVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "WebKitVC") as? WebKitViewController {
126 | webKitVC.urlString = kFormContactUrl
127 | self.navigationController?.pushViewController(webKitVC, animated: true)
128 | self.tabBarController?.hidePlayerView()
129 | }
130 | } else {
131 | if let url = URL(string: BASE_URL + "about"), let webVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AboutVC") as? AboutViewController {
132 | webVC.requestUrl = url
133 | self.navigationController?.pushViewController(webVC, animated: true)
134 | self.tabBarController?.hidePlayerView()
135 | }
136 | }
137 | }
138 |
139 | tableView.deselectRow(at: indexPath, animated: true)
140 | }
141 |
142 | }
143 |
144 | extension MoreTableViewController {
145 |
146 | @objc func loggingIn(_ notification:Notification) {
147 | let indexPath = IndexPath(row: 1, section: 0)
148 | guard let cell = self.tableView.cellForRow(at: indexPath) else {
149 | return
150 | }
151 |
152 | cell.textLabel?.text = nil
153 | let activityIndicator = UIActivityIndicatorView()
154 | activityIndicator.color = UIColor.black
155 | cell.addSubview(activityIndicator)
156 | activityIndicator.startAnimating()
157 | }
158 |
159 | @objc func loggedInSuccessfully(_ notification:Notification) {
160 | self.tableView.reloadData()
161 | }
162 |
163 | @objc func loginFailed(_ notification:Notification) {
164 | let indexPath = IndexPath(row: 1, section: 0)
165 | guard let cell = self.tableView.cellForRow(at: indexPath) else {
166 | return
167 | }
168 |
169 | for view in cell.subviews {
170 | if let activityIndicator = view as? UIActivityIndicatorView {
171 | activityIndicator.removeFromSuperview()
172 | }
173 | }
174 | cell.textLabel?.text = "Login"
175 | }
176 |
177 | @objc func loggedOutSuccessfully(_ notification:Notification) {
178 | self.tableView.reloadData()
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/Podverse/NowPlayingBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NowPlayingBar.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/15/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import StreamingKit
11 |
12 | protocol NowPlayingBarDelegate:class {
13 | func didTapView()
14 | }
15 |
16 | class NowPlayingBar:UIView {
17 | @IBOutlet weak var podcastImageView: UIImageView!
18 | @IBOutlet weak var podcastTitleLabel: UILabel!
19 | @IBOutlet weak var playButton: UIButton!
20 | @IBOutlet weak var episodeTitle: UILabel!
21 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
22 |
23 | weak var delegate:NowPlayingBarDelegate?
24 |
25 | let pvMediaPlayer = PVMediaPlayer.shared
26 |
27 | @IBAction func didTapView(_ sender: Any) {
28 | self.delegate?.didTapView()
29 | }
30 |
31 | @IBAction func playPause(_ sender: Any) {
32 | PVMediaPlayer.shared.playOrPause()
33 | }
34 |
35 | override init(frame: CGRect) {
36 | super.init(frame: frame)
37 | setupView()
38 | self.activityIndicator.hidesWhenStopped = true
39 | }
40 |
41 | required init?(coder aDecoder: NSCoder) {
42 | super.init(coder: aDecoder)
43 | setupView()
44 | self.activityIndicator.hidesWhenStopped = true
45 | }
46 |
47 | private func setupView() {
48 | let view = viewFromNibForClass()
49 | view.frame = bounds
50 |
51 | view.autoresizingMask = [
52 | UIViewAutoresizing.flexibleWidth,
53 | UIViewAutoresizing.flexibleHeight
54 | ]
55 |
56 | addSubview(view)
57 | }
58 |
59 | private func viewFromNibForClass() -> UIView {
60 | let bundle = Bundle(for: type(of: self))
61 | let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
62 | let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
63 |
64 | return view
65 | }
66 |
67 | static var playerHeight:CGFloat {
68 | return 60.0
69 | }
70 |
71 | func togglePlayIcon() {
72 | let audioPlayer = PVMediaPlayer.shared.audioPlayer
73 | DispatchQueue.main.async {
74 | if audioPlayer.state == .stopped || audioPlayer.state == .paused {
75 | self.activityIndicator.isHidden = true
76 | self.playButton.setImage(UIImage(named:"play"), for: .normal)
77 | self.playButton.tintColor = UIColor.black
78 | self.playButton.isHidden = false
79 | } else if audioPlayer.state == .error {
80 | self.activityIndicator.isHidden = true
81 | self.playButton.setImage(UIImage(named:"playerror"), for: .normal)
82 | // TODO: why doesn't this turn red? The playButton stays black for some reason. It works in MediaPlayerVC tho...
83 | self.playButton.tintColor = UIColor.red
84 | self.playButton.isHidden = false
85 | } else if audioPlayer.state == .playing && !self.pvMediaPlayer.shouldSetupClip {
86 | self.activityIndicator.isHidden = true
87 | self.playButton.setImage(UIImage(named:"pause"), for: .normal)
88 | self.playButton.tintColor = UIColor.black
89 | self.playButton.isHidden = false
90 | } else if audioPlayer.state == .buffering || self.pvMediaPlayer.shouldSetupClip {
91 | self.activityIndicator.isHidden = false
92 | self.playButton.isHidden = true
93 | } else {
94 | self.activityIndicator.isHidden = true
95 | self.playButton.setImage(UIImage(named:"play"), for: .normal)
96 | self.playButton.tintColor = UIColor.black
97 | self.playButton.isHidden = false
98 | }
99 | }
100 | }
101 |
102 | }
103 |
104 | extension NowPlayingBar:PVMediaPlayerUIDelegate {
105 |
106 | func playerHistoryItemBuffering() {
107 | self.togglePlayIcon()
108 | }
109 |
110 | func playerHistoryItemErrored() {
111 | self.togglePlayIcon()
112 | }
113 |
114 | func playerHistoryItemLoaded() {
115 | self.togglePlayIcon()
116 | }
117 |
118 | func playerHistoryItemLoadingBegan() {
119 | self.togglePlayIcon()
120 | }
121 |
122 | func playerHistoryItemPaused() {
123 | self.togglePlayIcon()
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/Podverse/PVPlayerHistoryManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PVPlayerHistoryManager.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 5/21/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class PlayerHistory {
12 | static let manager = PlayerHistory()
13 | var historyItems = [PlayerHistoryItem]() {
14 | didSet {
15 | self.saveData()
16 | }
17 | }
18 |
19 | //save data
20 | func saveData() {
21 | let data = NSMutableData()
22 | let archiver = NSKeyedArchiver(forWritingWith: data)
23 | archiver.encode(historyItems, forKey: "userHistory")
24 | archiver.finishEncoding()
25 | data.write(toFile: dataFilePath(), atomically: true)
26 | }
27 |
28 | //read data
29 | func loadData() {
30 | let path = self.dataFilePath()
31 | let defaultManager = FileManager()
32 | if defaultManager.fileExists(atPath: path) {
33 | let url = URL(fileURLWithPath: path)
34 | do {
35 | let data = try Data(contentsOf: url)
36 | let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
37 | if let hItems = unarchiver.decodeObject(forKey: "userHistory") as? Array {
38 | historyItems = hItems
39 | }
40 | unarchiver.finishDecoding()
41 | } catch {
42 | print("Decoding failed: \(error.localizedDescription)")
43 | }
44 |
45 |
46 | }
47 | }
48 |
49 | func documentsDirectory()->String {
50 | let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory,
51 | .userDomainMask, true)
52 | return paths.first ?? ""
53 | }
54 |
55 | func dataFilePath ()->String{
56 | return self.documentsDirectory().appendingFormat("/.plist")
57 | }
58 |
59 | func addOrUpdateItem(item: PlayerHistoryItem?) {
60 |
61 | if let item = item {
62 | let previousIndex = historyItems.index(where: { (previousItem) -> Bool in // thanks sschuth https://stackoverflow.com/a/24069331/2608858
63 | previousItem.episodeMediaUrl == item.episodeMediaUrl
64 | })
65 |
66 | if let index = previousIndex {
67 | historyItems[index] = item
68 | historyItems.rearrange(from: index, to: 0)
69 | } else {
70 | historyItems.insert(item, at: 0)
71 | }
72 | }
73 |
74 | }
75 |
76 | func retrieveExistingPlayerHistoryItem(mediaUrl: String) -> PlayerHistoryItem? {
77 |
78 | let previousIndex = historyItems.index(where: { (previousItem) -> Bool in // thanks sschuth https://stackoverflow.com/a/24069331/2608858
79 | previousItem.episodeMediaUrl == mediaUrl
80 | })
81 |
82 | if let index = previousIndex {
83 | return historyItems[index]
84 | }
85 |
86 | return nil
87 |
88 | }
89 |
90 | func convertSearchPodcastEpisodeToPlayerHistoryItem(searchPodcast: SearchPodcast, searchEpisode: SearchEpisode) -> PlayerHistoryItem {
91 | let playerHistoryItem = PlayerHistoryItem(
92 | podcastId: searchPodcast.id,
93 | podcastFeedUrl: nil, // Since it is a searchPodcast, we can use podcastId instead of podcastFeedUrl
94 | podcastTitle: searchPodcast.title,
95 | podcastImageUrl: searchPodcast.imageUrl,
96 | episodeId: searchEpisode.id,
97 | episodeMediaUrl: searchEpisode.mediaUrl,
98 | episodeTitle: searchEpisode.title,
99 | episodeSummary: searchEpisode.summary,
100 | episodePubDate: searchEpisode.pubDate?.toServerDate(),
101 | hasReachedEnd: false,
102 | lastPlaybackPosition: 0)
103 |
104 | return playerHistoryItem
105 | }
106 |
107 | func convertEpisodeToPlayerHistoryItem(episode: Episode) -> PlayerHistoryItem {
108 | let playerHistoryItem = PlayerHistoryItem(
109 | podcastId: episode.podcast.id,
110 | podcastFeedUrl: episode.podcast.feedUrl,
111 | podcastTitle: episode.podcast.title,
112 | podcastImageUrl: episode.podcast.imageUrl,
113 | episodeMediaUrl: episode.mediaUrl,
114 | episodeTitle: episode.title,
115 | episodeSummary: episode.summary,
116 | episodePubDate: episode.pubDate,
117 | hasReachedEnd: false,
118 | lastPlaybackPosition: 0)
119 |
120 | return playerHistoryItem
121 | }
122 |
123 | func convertMediaRefToPlayerHistoryItem(mediaRef: MediaRef) -> PlayerHistoryItem {
124 | let playerHistoryItem = PlayerHistoryItem(
125 | mediaRefId: mediaRef.id,
126 | podcastId: mediaRef.podcastId,
127 | podcastFeedUrl: mediaRef.podcastFeedUrl,
128 | podcastTitle: mediaRef.podcastTitle,
129 | podcastImageUrl: mediaRef.podcastImageUrl,
130 | episodeId: mediaRef.episodeId,
131 | episodeMediaUrl: mediaRef.episodeMediaUrl,
132 | episodeTitle: mediaRef.episodeTitle,
133 | episodeSummary: mediaRef.episodeSummary,
134 | episodePubDate: mediaRef.episodePubDate,
135 | startTime: mediaRef.startTime,
136 | endTime: mediaRef.endTime,
137 | clipTitle: mediaRef.title,
138 | ownerName: mediaRef.ownerName,
139 | ownerId: mediaRef.ownerId,
140 | hasReachedEnd: false,
141 | lastPlaybackPosition: 0,
142 | isPublic: mediaRef.isPublic)
143 |
144 | return playerHistoryItem
145 | }
146 |
147 | func checkIfPodcastWasLastPlayed(podcastId:String?, feedUrl:String?) -> Bool {
148 | if let _ = podcastId, historyItems.first?.podcastId == podcastId {
149 | return true
150 | } else if let feedUrl = feedUrl, historyItems.first?.podcastFeedUrl == feedUrl {
151 | return true
152 | } else {
153 | return false
154 | }
155 | }
156 |
157 | func checkIfEpisodeWasLastPlayed(mediaUrl: String) -> Bool {
158 | if historyItems.first?.episodeMediaUrl == mediaUrl {
159 | return true
160 | } else {
161 | return false
162 | }
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/Podverse/PVReachability.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PVReachability.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/25/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReachabilitySwift
11 |
12 | class PVReachability {
13 | static let shared = PVReachability()
14 | let reachability = Reachability()!
15 |
16 | init() {
17 | reachability.whenReachable = { reachability in
18 | if !reachability.isReachableViaWiFi
19 | {
20 | // self.pauseDownloadingEpisodesUntilWiFi()
21 | } else {
22 | // self.resumeDownloadingEpisodes()
23 | }
24 |
25 | if UserDefaults.standard.object(forKey: "ONE_TIME_LOGIN") != nil && UserDefaults.standard.bool(forKey: "DefaultPlaylistsCreated") == false {
26 | // TODO:
27 | // let playlistManager = PlaylistManager.sharedInstance
28 | // playlistManager.getMyPlaylistsFromServer({
29 | // playlistManager.createDefaultPlaylists()
30 | // })
31 | }
32 | }
33 |
34 | reachability.whenUnreachable = { reachability in
35 | if !reachability.isReachableViaWiFi {
36 | // self.pauseDownloadingEpisodesUntilWiFi()
37 | }
38 |
39 | DispatchQueue.main.async {
40 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kInternetIsUnreachable), object: self, userInfo: nil)
41 | }
42 |
43 | }
44 |
45 | do {
46 | try reachability.startNotifier()
47 | } catch {
48 | print("Unable to start reachability notifier")
49 | }
50 | }
51 |
52 | func hasInternetConnection() -> Bool {
53 | return reachability.isReachable
54 | }
55 |
56 | func hasWiFiConnection() -> Bool {
57 | return reachability.isReachableViaWiFi
58 | }
59 |
60 | // TODO: move to PVDownloader without causing splash screen to hang indefinitely
61 | // func pauseDownloadingEpisodesUntilWiFi() {
62 | // let downloader = PVDownloader.shared
63 | // downloader.downloadSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
64 | // for downloadingEpisode in DownloadingEpisodeList.shared.downloadingEpisodes {
65 | // if let taskIdentifier = downloadingEpisode.taskIdentifier {
66 | // for episodeDownloadTask in downloadTasks {
67 | // if episodeDownloadTask.taskIdentifier == taskIdentifier {
68 | // downloader.pauseOrResumeDownloadingEpisode(episode: downloadingEpisode)
69 | // }
70 | // }
71 | // }
72 | // }
73 | // }
74 | // }
75 |
76 | // TODO: move to PVDownloader without causing splash screen to hang indefinitely
77 | // func resumeDownloadingEpisodes() {
78 | // let downloader = PVDownloader.shared
79 | // downloader.downloadSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
80 | // for downloadingEpisode in DownloadingEpisodeList.shared.downloadingEpisodes {
81 | // if (downloadingEpisode.taskResumeData != nil || downloadingEpisode.pausedWithoutResumeData == true) && downloadingEpisode.pausedByUser == false {
82 | // downloader.pauseOrResumeDownloadingEpisode(episode: downloadingEpisode)
83 | // }
84 | // }
85 | // }
86 | // }
87 |
88 | func createInternetConnectionNeededAlertWithDescription(_ message: String) -> UIAlertController {
89 | let connectionNeededAlert = UIAlertController(title: "Internet Connection Needed", message: message, preferredStyle: UIAlertControllerStyle.alert)
90 | connectionNeededAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
91 | connectionNeededAlert.addAction(UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
92 | let settingsURL = URL(string: UIApplicationOpenSettingsURLString)
93 | if let url = settingsURL {
94 | UIApplication.shared.open(url)
95 | }
96 | })
97 | return connectionNeededAlert
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Podverse/PVSubscriber.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PVSubscriber.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 5/6/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
11 |
12 | class PVSubscriber {
13 |
14 | static func subscribeToPodcast(podcastId: String?, feedUrl: String?) {
15 |
16 | if let feedUrl = feedUrl {
17 |
18 | // TODO: add error handling / connectivity error
19 | updatePodcastOnServer(podcastId: podcastId, shouldSubscribe: true) { wasSuccessful in
20 | //
21 | }
22 |
23 | let feedParser = PVFeedParser(shouldOnlyGetMostRecentEpisode: false, shouldSubscribe: true, podcastId: podcastId)
24 | feedParser.addToParsingQueue(feedUrlString: feedUrl)
25 | }
26 |
27 | }
28 |
29 | static func unsubscribeFromPodcast(podcastId: String?, feedUrl: String?) {
30 |
31 | if let podcastId = podcastId {
32 |
33 | // TODO: add error handling / connectivity error
34 | updatePodcastOnServer(podcastId: podcastId, shouldSubscribe: false) { wasSuccessful in
35 | //
36 | }
37 |
38 | PVDeleter.deletePodcast(podcastId: podcastId, feedUrl: feedUrl)
39 |
40 | } else if let feedUrl = feedUrl {
41 | PVDeleter.deletePodcast(podcastId: nil, feedUrl: feedUrl)
42 | }
43 |
44 | }
45 |
46 | static func checkIfSubscribed(podcastId: String?) -> Bool {
47 | if let podcastId = podcastId, let _ = Podcast.podcastForId(id: podcastId) {
48 | return true
49 | } else {
50 | return false
51 | }
52 | }
53 |
54 | static func updatePodcastOnServer(podcastId:String?, shouldSubscribe:Bool, completion: @escaping (_ wasSuccessful:Bool?) -> Void) {
55 |
56 | let urlEnding = shouldSubscribe == true ? "subscribe" : "unsubscribe"
57 |
58 | if let podcastId = podcastId, let url = URL(string: BASE_URL + "podcasts/" + urlEnding) {
59 | var request = URLRequest(url: url, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30)
60 | request.httpMethod = "POST"
61 |
62 | guard let idToken = UserDefaults.standard.string(forKey: "idToken") else {
63 | DispatchQueue.main.async {
64 | completion(false)
65 | }
66 | return
67 | }
68 |
69 | request.setValue(idToken, forHTTPHeaderField: "Authorization")
70 |
71 | let postString = "podcastId=" + podcastId
72 | request.httpBody = postString.data(using: .utf8)
73 |
74 | showNetworkActivityIndicator()
75 |
76 | let task = URLSession.shared.dataTask(with: request) { data, response, error in
77 |
78 | hideNetworkActivityIndicator()
79 |
80 | guard error == nil else {
81 | DispatchQueue.main.async {
82 | completion(false)
83 | }
84 | return
85 | }
86 |
87 | DispatchQueue.main.async {
88 | completion(true)
89 | }
90 |
91 | }
92 |
93 | task.resume()
94 |
95 | }
96 |
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/Podverse/PVTimeHelpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PVTimeHelpers.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/24/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class PVTimeHelper {
12 |
13 | static func convertIntToHMSString (time : Int?) -> String {
14 |
15 | guard let time = time else {
16 | return ""
17 | }
18 |
19 | var hours = String(time / 3600) + ":"
20 | if hours == "0:" {
21 | hours = ""
22 | }
23 | var minutes = String((time / 60) % 60) + ":"
24 | if minutes.count < 3 && hours != "" {
25 | minutes = "0" + minutes
26 | }
27 | var seconds = String(time % 60)
28 | if seconds.count < 2 && (hours != "" || minutes != "") {
29 | seconds = "0" + seconds
30 | }
31 |
32 | return "\(hours)\(minutes)\(seconds)"
33 | }
34 |
35 | static func convertHMSStringToInt(hms : String) -> Int {
36 | var hmsComponents = hms.components(separatedBy:":").reversed().map() { String($0) }
37 | var seconds = 0
38 | var minutes = 0
39 | var hours = 0
40 | if let secondsVal = hmsComponents.first, let sec = Int(secondsVal) {
41 | seconds = sec
42 | hmsComponents.removeFirst()
43 | }
44 |
45 | if let minutesVal = hmsComponents.first, let min = Int(minutesVal) {
46 | minutes = min
47 | hmsComponents.removeFirst()
48 | }
49 |
50 | if let hoursVal = hmsComponents.first, let hr = Int(hoursVal) {
51 | hours = hr
52 | hmsComponents.removeFirst()
53 | }
54 |
55 | return convertHMSIntsToSeconds(hms:(hours, minutes, seconds))
56 | }
57 |
58 |
59 | static func convertIntToHMSInts (seconds : Int) -> (Int, Int, Int) {
60 | return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
61 | }
62 |
63 | static func convertHMSIntsToSeconds(hms:(Int,Int,Int)) -> Int {
64 | let hoursInSeconds = hms.0 * 3600
65 | let minutesInSeconds = hms.2 * 60
66 | let totalSeconds = hoursInSeconds + minutesInSeconds + hms.2
67 |
68 | return totalSeconds
69 | }
70 |
71 | static func convertIntToReadableHMSDuration(seconds: Int) -> String {
72 | var string = ""
73 | let hmsInts = convertIntToHMSInts(seconds: seconds)
74 |
75 | if hmsInts.0 > 0 {
76 | string += String(hmsInts.0) + "h "
77 | }
78 |
79 | if hmsInts.1 > 0 {
80 | string += String(hmsInts.1) + "m "
81 | }
82 |
83 | if hmsInts.2 > 0 {
84 | string += String(hmsInts.2) + "s"
85 | }
86 |
87 | return string.trimmingCharacters(in: .whitespaces)
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/Podverse/PVViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PVViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 5/8/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import StreamingKit
11 | import SDWebImage
12 |
13 | protocol TableViewHeightProtocol:class {
14 | func adjustTableView()
15 | }
16 |
17 | class PVViewController: UIViewController {
18 |
19 | let playerHistoryManager = PlayerHistory.manager
20 | let pvMediaPlayer = PVMediaPlayer.shared
21 | static weak var delegate:TableViewHeightProtocol?
22 |
23 | override func viewDidAppear(_ animated: Bool) {
24 | super.viewDidAppear(animated)
25 | }
26 |
27 | override func viewDidLoad() {
28 | super.viewDidLoad()
29 | self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style:.plain, target:nil, action:nil)
30 | addObservers()
31 |
32 | }
33 |
34 | override func viewWillAppear(_ animated: Bool) {
35 | super.viewWillAppear(animated)
36 | pvMediaPlayer.delegate = self.tabBarController?.playerView
37 | PVViewController.delegate = self
38 | toggleNowPlayingBar()
39 | }
40 |
41 | deinit {
42 | removeObservers()
43 | }
44 |
45 | fileprivate func addObservers() {
46 | NotificationCenter.default.addObserver(self, selector: #selector(self.episodeDeleted(_:)), name: .episodeDeleted, object: nil)
47 | NotificationCenter.default.addObserver(self, selector: #selector(self.podcastDeleted(_:)), name: .podcastDeleted, object: nil)
48 | }
49 |
50 | fileprivate func removeObservers() {
51 | NotificationCenter.default.removeObserver(self, name: .episodeDeleted, object: nil)
52 | NotificationCenter.default.removeObserver(self, name: .podcastDeleted, object: nil)
53 | }
54 |
55 | func toggleNowPlayingBar() {
56 | self.updateNowPlayingBarData() { shouldShow in
57 | if shouldShow {
58 | self.tabBarController?.showPlayerView()
59 | } else {
60 | self.tabBarController?.hidePlayerView()
61 | }
62 | }
63 | }
64 |
65 | func hideNowPlayingBar() {
66 | self.tabBarController?.hidePlayerView()
67 | }
68 |
69 | func updateNowPlayingBarData(completion: @escaping (_ shouldShow: Bool) -> Void) {
70 | DispatchQueue.main.async {
71 | guard let currentItem = self.playerHistoryManager.historyItems.first, let tabbarVC = self.tabBarController, PVMediaPlayer.shared.nowPlayingItem != nil && currentItem.hasReachedEnd != true else {
72 | completion(false)
73 | return
74 | }
75 |
76 | tabbarVC.playerView.podcastTitleLabel.text = currentItem.podcastTitle?.stringByDecodingHTMLEntities()
77 | tabbarVC.playerView.episodeTitle.text = currentItem.episodeTitle?.stringByDecodingHTMLEntities()
78 |
79 | tabbarVC.playerView.podcastImageView.image = Podcast.retrievePodcastImage(podcastImageURLString: currentItem.podcastImageUrl, feedURLString: currentItem.podcastFeedUrl, completion: { image in
80 | tabbarVC.playerView.podcastImageView.image = image
81 | })
82 |
83 | tabbarVC.playerView.togglePlayIcon()
84 |
85 | completion(true)
86 | }
87 | }
88 |
89 | func goToNowPlaying() {
90 | self.tabBarController?.goToNowPlaying()
91 | }
92 |
93 | }
94 |
95 | extension PVViewController {
96 | @objc func episodeDeleted(_ notification:Notification) {
97 | if let mediaUrl = notification.userInfo?["mediaUrl"] as? String {
98 | if playerHistoryManager.checkIfEpisodeWasLastPlayed(mediaUrl: mediaUrl) == true {
99 | DispatchQueue.main.async {
100 | self.tabBarController?.hidePlayerView()
101 | }
102 | }
103 | }
104 | }
105 |
106 | @objc func podcastDeleted(_ notification:Notification) {
107 |
108 | if let podcastId = notification.userInfo?["podcastId"] as? String, playerHistoryManager.checkIfPodcastWasLastPlayed(podcastId: podcastId, feedUrl: nil) == true {
109 | DispatchQueue.main.async {
110 | self.tabBarController?.hidePlayerView()
111 | }
112 | }
113 |
114 |
115 | if let feedUrl = notification.userInfo?["feedUrl"] as? String, playerHistoryManager.checkIfPodcastWasLastPlayed(podcastId: nil, feedUrl: feedUrl) == true {
116 | DispatchQueue.main.async {
117 | self.tabBarController?.hidePlayerView()
118 | }
119 | }
120 | }
121 | }
122 |
123 | extension PVViewController:TableViewHeightProtocol {
124 | func adjustTableView() {
125 | if let index = self.view.constraints.index(where: {$0.secondItem is UITableView && $0.secondAttribute == NSLayoutAttribute.bottom }),
126 | let tabbarVC = self.tabBarController {
127 | self.view.constraints[index].constant = tabbarVC.playerView.isHidden ? 0.0 : tabbarVC.playerView.frame.height - 0.5
128 | } else if let index = self.view.constraints.index(where: {$0.secondItem is UITableView && $0.secondAttribute == NSLayoutAttribute.bottom }) {
129 | self.view.constraints[index].constant = self.tabBarController?.tabBar.frame.height ?? 0.0
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Podverse/ParsingPodcasts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParsingPodcastUrls.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 9/19/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import TaskQueue
12 |
13 | final class ParsingPodcasts {
14 | static let shared = ParsingPodcasts()
15 | var podcastKeys = [String]() {
16 | didSet {
17 | DispatchQueue.main.async {
18 | UIApplication.shared.isNetworkActivityIndicatorVisible = (self.podcastKeys.count > 0)
19 | }
20 | }
21 | }
22 | var currentlyParsingItem = 0
23 |
24 | let queue = TaskQueue()
25 |
26 | init() {
27 | self.queue.maximumNumberOfActiveTasks = 2
28 | }
29 |
30 | func clearParsingPodcastsIfFinished() {
31 | if currentlyParsingItem == podcastKeys.count {
32 | currentlyParsingItem = 0
33 | self.podcastKeys.removeAll()
34 | DispatchQueue.main.async {
35 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kFinishedAllParsingPodcasts), object: self, userInfo: nil)
36 | }
37 | }
38 | }
39 |
40 | func addPodcast(podcastId:String?, feedUrl:String?) {
41 | if let podcastId = podcastId, self.podcastKeys.filter({$0 == podcastId}).count < 1 {
42 | self.podcastKeys.append(podcastId)
43 | DispatchQueue.main.async {
44 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kBeginParsingPodcast), object: self, userInfo: nil)
45 | }
46 | } else if let feedUrl = feedUrl, self.podcastKeys.filter({$0 == feedUrl}).count < 1, podcastId == nil {
47 | self.podcastKeys.append(feedUrl)
48 | DispatchQueue.main.async {
49 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kBeginParsingPodcast), object: self, userInfo: nil)
50 | }
51 | }
52 | }
53 |
54 | func removePodcast(podcastId:String?, feedUrl:String?) {
55 | if let podcastId = podcastId, let index = self.podcastKeys.index(of: podcastId) {
56 | self.podcastKeys.remove(at: index)
57 | DispatchQueue.main.async {
58 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kFinishedParsingPodcast), object: self, userInfo: nil)
59 | }
60 | } else if let feedUrl = feedUrl, let index = self.podcastKeys.index(of: feedUrl) {
61 | self.podcastKeys.remove(at: index)
62 | DispatchQueue.main.async {
63 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kFinishedParsingPodcast), object: self, userInfo: nil)
64 | }
65 | }
66 | }
67 |
68 | func hasMatchingId(podcastId:String?) -> Bool {
69 | if let podcastId = podcastId, let _ = self.podcastKeys.index(of: podcastId) {
70 | return true
71 | }
72 |
73 | return false
74 | }
75 |
76 | func hasMatchingUrl(feedUrl:String) -> Bool {
77 | if let _ = self.podcastKeys.index(of: feedUrl) {
78 | return true
79 | }
80 |
81 | return false
82 | }
83 |
84 | func podcastFinishedParsing() {
85 | self.currentlyParsingItem += 1
86 | clearParsingPodcastsIfFinished()
87 | DispatchQueue.main.async {
88 | NotificationCenter.default.post(name:NSNotification.Name(rawValue: kFinishedParsingPodcast), object: self, userInfo: nil)
89 | }
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/Podverse/PlaylistDetailTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlaylistDetailTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 8/23/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class PlaylistDetailTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var clipTitle: UILabel!
14 | @IBOutlet weak var episodeTitle: UILabel!
15 | @IBOutlet weak var podcastImage: UIImageView!
16 | @IBOutlet weak var podcastTitle: UILabel!
17 | @IBOutlet weak var pubDate: UILabel!
18 | @IBOutlet weak var time: UILabel!
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Podverse/PlaylistTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlaylistTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 8/22/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class PlaylistTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var itemCount: UILabel!
14 | @IBOutlet weak var lastUpdated: UILabel!
15 | @IBOutlet weak var title: UILabel!
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Podverse/PodcastSearchResultTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PodcastSearchResultTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/9/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class PodcastSearchResultTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var pvImage: UIImageView!
14 | @IBOutlet weak var title: UILabel!
15 | @IBOutlet weak var hosts: UILabel!
16 | @IBOutlet weak var categories: UILabel!
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Podverse/PodcastTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PodcastTableViewCell.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/27/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class PodcastTableViewCell: UITableViewCell {
12 |
13 | @IBOutlet weak var autoDownloadIndicator: UILabel!
14 | @IBOutlet weak var lastPublishedDate: UILabel!
15 | @IBOutlet weak var pvImage: UIImageView!
16 | @IBOutlet weak var title: UILabel!
17 | @IBOutlet weak var totalEpisodes: UILabel!
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Podverse/Podverse.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.associated-domains
6 |
7 | applinks:podverse.fm
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Podverse/SearchCategory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchCategory.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 10/23/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | //import Foundation
10 | //
11 | //class SearchCategory {
12 | //
13 | // var id:Int64?
14 | // var name:String = ""
15 | // var parent_id:Int64?
16 | //
17 | // static func convertJSONToSearchCategories(_ json: AnyObject) -> [SearchCategory]? {
18 | //
19 | // var categories = [SearchCategory]()
20 | //
21 | // if let items = json as? [AnyObject] {
22 | // for item in items {
23 | // let category = SearchCategory()
24 | // category.id = item["id"] as? Int64
25 | // category.name = item["name"] as? String ?? ""
26 | // category.parent_id = item["parent_id"] as? Int64
27 | // categories.append(category)
28 | // }
29 | // }
30 | //
31 | // return categories
32 | //
33 | // }
34 | //
35 | // static func retrieveCategoriesFromServer(parentId: Int64?, _ completion: @escaping (_ categories:[SearchCategory]?) -> Void) {
36 | //
37 | // SearchClientSwift.retrieveCategories({ serviceResponse in
38 | // if let response = serviceResponse.0, let categories = SearchCategory.convertJSONToSearchCategories(response) {
39 | // completion(categories)
40 | // }
41 | //
42 | // if let error = serviceResponse.1 {
43 | // print(error.localizedDescription)
44 | // completion(nil)
45 | // }
46 | //
47 | // })
48 | //
49 | // }
50 | //
51 | // static func filterCategories(categories: [SearchCategory] , parentId: Int64?) -> [SearchCategory] {
52 | //
53 | // var filteredCategories = [SearchCategory]()
54 | //
55 | // if let parentId = parentId {
56 | // filteredCategories = categories.filter { $0.parent_id == parentId }
57 | // } else {
58 | // filteredCategories = categories.filter { $0.parent_id == nil }
59 | // }
60 | //
61 | // filteredCategories.sort(by: { $0.name < $1.name })
62 | //
63 | // return filteredCategories
64 | //
65 | // }
66 | //
67 | //}
68 | //
69 |
--------------------------------------------------------------------------------
/Podverse/SearchEpisode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchEpisode.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 1/25/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class SearchEpisode {
12 |
13 | var id: String?
14 | var isPublic: Bool = false
15 | var duration: NSNumber?
16 | var mediaUrl: String?
17 | var pubDate: String?
18 | var summary: String?
19 | var title: String?
20 |
21 | static func convertJSONToSearchEpisode (_ json: [String:Any]) -> SearchEpisode {
22 |
23 | let searchEpisode = SearchEpisode()
24 |
25 | if let id = json["id"] as? String {
26 | searchEpisode.id = id
27 | }
28 |
29 | if let isPublic = json["isPublic"] as? Bool {
30 | searchEpisode.isPublic = isPublic
31 | }
32 |
33 | if let mediaUrl = json["mediaUrl"] as? String {
34 | searchEpisode.mediaUrl = mediaUrl
35 | }
36 |
37 | if let pubDate = json["pubDate"] as? String {
38 | searchEpisode.pubDate = pubDate
39 | }
40 |
41 | if let summary = json["summary"] as? String {
42 | searchEpisode.summary = summary
43 | }
44 |
45 | if let title = json["title"] as? String {
46 | searchEpisode.title = title
47 | }
48 |
49 | if let duration = json["duration"] as? NSNumber {
50 | searchEpisode.duration = duration
51 | }
52 |
53 | return searchEpisode
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Podverse/SearchNetwork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchNetwork.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 10/23/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | //import Foundation
10 | //
11 | //class SearchNetwork {
12 | //
13 | // var id:Int64?
14 | // var name:String?
15 | //
16 | // static func convertJSONToSearchNetworks(_ json: AnyObject) -> [SearchNetwork]? {
17 | //
18 | // var networks = [SearchNetwork]()
19 | //
20 | // if let items = json as? [AnyObject] {
21 | // for item in items {
22 | // let network = SearchNetwork()
23 | // network.id = item["id"] as? Int64
24 | // network.name = item["name"] as? String
25 | // networks.append(network)
26 | // }
27 | // }
28 | //
29 | // return networks
30 | //
31 | // }
32 | //
33 | // static func retrieveNetworksFromServer(_ completion: @escaping (_ networks:[SearchNetwork]?) -> Void) {
34 | //
35 | // SearchClientSwift.retrieveNetworks({ serviceResponse in
36 | //
37 | // if let response = serviceResponse.0, let networks = SearchNetwork.convertJSONToSearchNetworks(response) {
38 | // completion(networks)
39 | // } else if let error = serviceResponse.1 {
40 | // print(error.localizedDescription)
41 | // completion(nil)
42 | // }
43 | //
44 | // })
45 | //
46 | // }
47 | //
48 | //}
49 |
50 |
--------------------------------------------------------------------------------
/Podverse/SettingsTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsTableViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/3/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SettingsTableViewController: UITableViewController {
12 |
13 | @IBOutlet weak var allowCellularDataSwitch: UISwitch!
14 | @IBOutlet weak var enableAutoDownloadSwitch: UISwitch!
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | self.title = "Settings"
20 | self.tableView.alwaysBounceVertical = false
21 | self.allowCellularDataSwitch.isOn = UserDefaults.standard.bool(forKey: kAllowCellularDataDownloads)
22 | self.enableAutoDownloadSwitch.isOn = UserDefaults.standard.bool(forKey: kEnableAutoDownloadByDefault)
23 | }
24 |
25 | @IBAction func allowCellularDataAction(_ sender: Any) {
26 | UserDefaults.standard.set(allowCellularDataSwitch.isOn, forKey: kAllowCellularDataDownloads)
27 | }
28 |
29 | @IBAction func enableAutoDownloadAction(_ sender: Any) {
30 | UserDefaults.standard.set(enableAutoDownloadSwitch.isOn, forKey: kEnableAutoDownloadByDefault)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Podverse/String+CharacterManipulation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+CharacterManipulation.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/27/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension String {
13 |
14 | func formatHtmlString(isWhiteBg: Bool = false) -> String {
15 |
16 | var style = ""
17 |
18 | if isWhiteBg {
19 | style = ""
20 | } else {
21 | style = ""
22 | }
23 |
24 | return style + self
25 | }
26 |
27 | func removeArticles() -> String {
28 | var words = self.components(separatedBy: " ")
29 |
30 | //Only one word so count it as sortable
31 | if(words.count <= 1) {
32 | return self
33 | }
34 |
35 | if( words[0].lowercased() == "a" || words[0].lowercased() == "the" || words[0].lowercased() == "an" ) {
36 | words.removeFirst()
37 | return words.joined(separator: " ")
38 | }
39 |
40 | return self
41 | }
42 |
43 | func removeHTMLFromString() -> String? {
44 | return self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)
45 | }
46 |
47 | func encodePipeInString () -> String {
48 | return self.replacingOccurrences(of:"|", with:"%7C", options: .literal, range: nil)
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Podverse/String+ConvertPlaybackTimeToUrlSchemeElements.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+ConvertTimeStampsToUrlSchemeElements.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/22/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 |
13 | func convertPlaybackTimesToUrlSchemeElements() -> String? {
14 | var newSelf = self
15 |
16 | do {
17 | let pattern = "([0-9][0-9]:[0-5][0-9]:[0-5][0-9]|[0-9]:[0-5][0-9]:[0-5][0-9]|[0-5][0-9]:[0-5][0-9]|[0-9]:[0-5][0-9])"
18 | let regex = try NSRegularExpression(pattern: pattern, options: [])
19 | let matches = regex.matches(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
20 | var indexOffset = 0
21 |
22 | for match in matches as [NSTextCheckingResult] {
23 | let location = match.range.location + indexOffset
24 | let length = match.range.length
25 | let range = NSMakeRange(location, length)
26 | if let strRange = Range(range, in: newSelf) {
27 | let urlSchemeElement = convertPlaybackTimeToUrlSchemeElement(timestamp: String(newSelf[strRange]))
28 | newSelf = newSelf.replacingCharacters(in: strRange, with: urlSchemeElement)
29 | indexOffset += urlSchemeElement.count - length
30 | }
31 | }
32 | } catch {
33 | print(error)
34 | }
35 |
36 | return newSelf
37 | }
38 |
39 | private func convertPlaybackTimeToUrlSchemeElement(timestamp: String) -> String {
40 | return "" + timestamp + ""
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Podverse/String+Formatters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Formatters.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/29/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 | func toServerDate() -> Date? {
13 | let dateFormatter = DateFormatter()
14 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
15 | return dateFormatter.date(from: self)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Podverse/String+MediaPlayerTimeToSeconds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+MediaPlayerTimeToSeconds.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/22/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 | func mediaPlayerTimeToSeconds() -> Int64 {
13 | let timeComponents = self.components(separatedBy: ":")
14 | var int = Int64(0)
15 |
16 | if timeComponents.count == 2, let minutes = Int64(timeComponents[0]), let seconds = Int64(timeComponents[1]) {
17 | int = minutes * 60 + seconds
18 | } else if timeComponents.count == 3, let hours = Int64(timeComponents[0]), let minutes = Int64(timeComponents[1]), let seconds = Int64(timeComponents[2]) {
19 | int = hours * 3600 + minutes * 60 + seconds
20 | }
21 |
22 | return int
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Podverse/String+RemoveHTMLCharacters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+RemoveHTMLCharacters.swift
3 | //
4 | // Created by Nacho on 29/10/14.
5 | // Copyright (c) 2014 Ignacio Nieto Carvajal. All rights reserved.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension String {
12 |
13 | /**
14 | * A pretty basic and simple decoder that will change the HTML entities for UTF8 ready characters.
15 | */
16 | func stringByDecodingHTMLEntities() -> String? {
17 | var r: NSRange
18 | let pattern = "<[^>]+>"
19 | var s = self.stringByDecodingHTMLEscapeCharacters()
20 | r = (s as NSString).range(of: pattern, options: NSString.CompareOptions.regularExpression)
21 | while (r.location != NSNotFound) {
22 | s = (s as NSString).replacingCharacters(in: r, with: " ")
23 | r = (s as NSString).range(of: pattern, options: NSString.CompareOptions.regularExpression)
24 | }
25 | return s.replacingOccurrences(of: " ", with: " ")
26 | }
27 |
28 | func stringByDecodingHTMLEscapeCharacters() -> String {
29 | var s = self.replacingOccurrences(of: """, with: "\"")
30 | s = s.replacingOccurrences(of: "'", with: "'")
31 | s = s.replacingOccurrences(of: "&", with: "&")
32 | s = s.replacingOccurrences(of: "<", with: "<")
33 | s = s.replacingOccurrences(of: ">", with: ">")
34 | s = s.replacingOccurrences(of: "'", with: "'")
35 | s = s.replacingOccurrences(of: "&ldquot;", with: "\"")
36 | s = s.replacingOccurrences(of: "&rdquot;", with: "\"")
37 | s = s.replacingOccurrences(of: " ", with: " ")
38 | s = s.replacingOccurrences(of: "á", with: "á")
39 | s = s.replacingOccurrences(of: "é", with: "é")
40 | s = s.replacingOccurrences(of: "í", with: "í")
41 | s = s.replacingOccurrences(of: "ó", with: "ó")
42 | s = s.replacingOccurrences(of: "ú", with: "ú")
43 | s = s.replacingOccurrences(of: "ñ", with: "ñ")
44 | s = s.replacingOccurrences(of: "’", with: "'")
45 |
46 | return s
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Podverse/String+Utilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Utilities.swift
3 | // FeedParser
4 | //
5 | // Created by Ignacio Nieto Carvajal on 9/10/16.
6 | // Copyright © 2016 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 | func substringBetween(initial: String, final: String) -> String? {
13 | guard var startIndex = self.range(of: initial)?.lowerBound else { return nil } // initial string not found
14 | startIndex = self.index(startIndex, offsetBy: 1)
15 | guard let endIndex = self.range(of: final, options: [] , range: Range(uncheckedBounds: (startIndex, self.endIndex)), locale: nil)?.lowerBound else { return nil } // final string not found from initial.
16 | let range = Range(uncheckedBounds: (startIndex, endIndex))
17 | return self.substring(with: range)
18 | }
19 |
20 | func toFailableBool() -> Bool? {
21 | switch self.lowercased() {
22 | case "true", "t", "yes", "y", "1":
23 | return true
24 | case "false", "f", "no", "n", "0":
25 | return false
26 | default:
27 | return nil
28 | }
29 | }
30 |
31 | func toBool() -> Bool {
32 | switch self.lowercased() {
33 | case "true", "t", "yes", "y", "1":
34 | return true
35 | case "false", "f", "no", "n", "0":
36 | return false
37 | default:
38 | return false
39 | }
40 | }
41 |
42 | func toDouble() -> Double {
43 | let numberFormatter = NumberFormatter()
44 | numberFormatter.locale = Locale(identifier: "en_US_POSIX")
45 | return numberFormatter.number(from: self.replacingOccurrences(of: ",", with: "."))?.doubleValue ?? 0.0
46 | }
47 |
48 | func toFailableDouble() -> Double? {
49 | let numberFormatter = NumberFormatter()
50 | numberFormatter.locale = Locale(identifier: "en_US_POSIX")
51 | return numberFormatter.number(from: self.replacingOccurrences(of: ",", with: "."))?.doubleValue ?? 0.0
52 | }
53 |
54 | func toInt() -> Int {
55 | return NumberFormatter().number(from: self)?.intValue ?? 0
56 | }
57 |
58 | func toFailableInt() -> Int? {
59 | return NumberFormatter().number(from: self)?.intValue
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Podverse/Supporting Libraries/OAuth2RetryHandler.swift:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/podverse/podverse-ios/fa55a169670731bcbd9073f16f75014d9b74c0fd/Podverse/Supporting Libraries/OAuth2RetryHandler.swift
--------------------------------------------------------------------------------
/Podverse/SyncablePodcast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SyncablePodcast.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 1/20/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import Foundation
12 | import UIKit
13 |
14 | class SyncablePodcast {
15 |
16 | var feedUrl: String?
17 | var id: String?
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/Podverse/UITabbarController+PlayerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITabbarController+PlayerView.swift
3 | // Podverse
4 | //
5 | // Created by Creon Creonopoulos on 7/16/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol PlayerViewProtocol {
12 | func setupPlayerBar()
13 | func hidePlayerView()
14 | func showPlayerView()
15 | func goToNowPlaying(isDataAvailable:Bool)
16 | var playerView:NowPlayingBar {get}
17 | }
18 |
19 | private var pvAssociationKey: UInt8 = 0
20 |
21 | extension UITabBarController:PlayerViewProtocol {
22 |
23 | var playerView:NowPlayingBar {
24 | get {
25 | return objc_getAssociatedObject(self, &pvAssociationKey) as! NowPlayingBar
26 | }
27 | set(newValue) {
28 | objc_setAssociatedObject(self, &pvAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
29 | }
30 | }
31 |
32 | override open func viewDidLoad() {
33 | super.viewDidLoad()
34 | self.playerView = NowPlayingBar()
35 | setupPlayerBar()
36 | }
37 |
38 | func setupPlayerBar() {
39 | var extraIphoneXSpace:CGFloat = 0.0
40 | if (UIScreen.main.nativeBounds.height == 2436.0) {
41 | extraIphoneXSpace = 33.0
42 | }
43 |
44 | self.playerView.frame = CGRect(x: self.tabBar.frame.minX,
45 | y: self.tabBar.frame.minY - NowPlayingBar.playerHeight - extraIphoneXSpace,
46 | width: self.tabBar.frame.width,
47 | height: NowPlayingBar.playerHeight)
48 | self.playerView.isHidden = true
49 | self.view.addSubview(self.playerView)
50 | self.playerView.delegate = self
51 | }
52 |
53 | func hidePlayerView() {
54 | self.playerView.isHidden = true
55 | PVViewController.delegate?.adjustTableView()
56 | }
57 |
58 | func showPlayerView() {
59 | self.playerView.isHidden = false
60 | PVViewController.delegate?.adjustTableView()
61 | }
62 |
63 | func goToNowPlaying(isDataAvailable:Bool = true) {
64 | if let currentNavVC = self.selectedViewController?.childViewControllers.first?.navigationController {
65 |
66 | var optionalMediaPlayerVC: MediaPlayerViewController?
67 |
68 | for controller in currentNavVC.viewControllers {
69 | if controller.isKind(of: MediaPlayerViewController.self) {
70 | optionalMediaPlayerVC = controller as? MediaPlayerViewController
71 | break
72 | }
73 | }
74 |
75 | if let mediaPlayerVC = optionalMediaPlayerVC {
76 | currentNavVC.popToViewController(mediaPlayerVC, animated: false)
77 | } else if let mediaPlayerVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MediaPlayerVC") as? MediaPlayerViewController {
78 | PVMediaPlayer.shared.isDataAvailable = isDataAvailable
79 | if !isDataAvailable {
80 | PVMediaPlayer.shared.shouldAutoplayOnce = true
81 | }
82 |
83 | currentNavVC.pushViewController(mediaPlayerVC, animated: true)
84 | }
85 | }
86 | }
87 |
88 | func goToClips(_ clipFilter:ClipFilter? = nil, _ clipSorting:ClipSorting? = nil) {
89 | if let currentNavVC = self.selectedViewController?.childViewControllers.first?.navigationController {
90 |
91 | var optionalClipsTVC: ClipsTableViewController?
92 |
93 | for controller in currentNavVC.viewControllers {
94 | if controller.isKind(of: ClipsTableViewController.self) {
95 | optionalClipsTVC = controller as? ClipsTableViewController
96 | break
97 | }
98 | }
99 |
100 | if let clipFilter = clipFilter {
101 | UserDefaults.standard.set(clipFilter.rawValue, forKey: kClipsTableFilterType)
102 | } else {
103 | UserDefaults.standard.set(ClipFilter.allPodcasts.rawValue, forKey: kClipsTableFilterType)
104 | }
105 |
106 | if let clipSorting = clipSorting {
107 | UserDefaults.standard.set(clipSorting.rawValue, forKey: kClipsTableSortingType)
108 | } else {
109 | UserDefaults.standard.set(ClipSorting.topWeek.rawValue, forKey: kClipsTableFilterType)
110 | }
111 |
112 | if let clipsTVC = optionalClipsTVC {
113 | clipsTVC.shouldOverrideQuery = true
114 | currentNavVC.popToViewController(clipsTVC, animated: false)
115 | } else if let clipsTVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ClipsTVC") as? ClipsTableViewController {
116 | currentNavVC.pushViewController(clipsTVC, animated: false)
117 | }
118 | }
119 | }
120 |
121 |
122 | func goToPlaylistDetail(id:String) {
123 | if let currentNavVC = self.selectedViewController?.childViewControllers.first?.navigationController {
124 |
125 | if let playlistDetailVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PlaylistDetailVC") as? PlaylistDetailTableViewController {
126 | playlistDetailVC.playlistId = id
127 | playlistDetailVC.isDataAvailable = false
128 | currentNavVC.pushViewController(playlistDetailVC, animated: true)
129 | }
130 | }
131 | }
132 |
133 | func gotoSearchPodcastView(id:String) {
134 | if let currentNavVC = self.selectedViewController?.childViewControllers.first?.navigationController {
135 |
136 | if let searchPodcastVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SearchPodcastVC") as? SearchPodcastViewController {
137 | searchPodcastVC.podcastId = id
138 | currentNavVC.pushViewController(searchPodcastVC, animated: true)
139 | }
140 | }
141 | }
142 | }
143 |
144 | extension UITabBarController:NowPlayingBarDelegate {
145 | func didTapView() {
146 | goToNowPlaying()
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Podverse/UITableView+NotAvailableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITableView+NotAvailableView.swift
3 | // Podverse
4 | //
5 | // Created by Creon Creonopoulos on 9/19/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIViewController {
12 |
13 | func showNoDataView() {
14 | let noDataView = self.view.viewWithTag(kNoDataViewTag)
15 | noDataView?.isHidden = false
16 | }
17 |
18 | func hideNoDataView() {
19 | let noDataView = self.view.viewWithTag(kNoDataViewTag)
20 | noDataView?.isHidden = true
21 | }
22 |
23 | func addNoDataViewWithMessage(_ message:String, buttonTitle:String? = nil, buttonImage:UIImage? = nil, retryPressed:Selector? = nil, isBlackBg:Bool = false) {
24 | let noDataView = UIView()
25 | let noDataTextLabel = UILabel()
26 | let actionButton = UIButton()
27 |
28 | noDataView.translatesAutoresizingMaskIntoConstraints = false
29 | noDataView.backgroundColor = isBlackBg ? .black : .white
30 | noDataView.tag = kNoDataViewTag
31 | actionButton.translatesAutoresizingMaskIntoConstraints = false
32 | noDataTextLabel.translatesAutoresizingMaskIntoConstraints = false
33 |
34 | noDataTextLabel.text = message
35 | noDataTextLabel.textColor = isBlackBg ? .white : .black
36 | noDataTextLabel.numberOfLines = 5
37 | noDataTextLabel.textAlignment = .center
38 | actionButton.setTitle(buttonTitle, for: .normal)
39 | actionButton.setTitleColor(.blue, for: .normal)
40 |
41 | if let image = buttonImage {
42 | actionButton.setImage(image, for: .normal)
43 | }
44 |
45 | if let retryAction = retryPressed {
46 | actionButton.addTarget(self, action: retryAction, for: .touchUpInside)
47 | }
48 |
49 | self.view.addSubview(noDataView)
50 |
51 | let containerLeading = NSLayoutConstraint( item:self.view,
52 | attribute:.leading,
53 | relatedBy:.equal,
54 | toItem:noDataView,
55 | attribute:.leading,
56 | multiplier:1,
57 | constant:0)
58 |
59 | let containerTrailing = NSLayoutConstraint( item:self.view,
60 | attribute:.trailing,
61 | relatedBy:.equal,
62 | toItem:noDataView,
63 | attribute:.trailing,
64 | multiplier:1,
65 | constant:0)
66 |
67 | let containerTop = NSLayoutConstraint( item:self.view,
68 | attribute:.top,
69 | relatedBy:.equal,
70 | toItem:noDataView,
71 | attribute:.top,
72 | multiplier:1,
73 | constant:0)
74 |
75 | let containerBottom = NSLayoutConstraint( item:self.view,
76 | attribute:.bottom,
77 | relatedBy:.equal,
78 | toItem:noDataView,
79 | attribute:.bottom,
80 | multiplier:1,
81 | constant:0)
82 |
83 | self.view.addConstraints([containerTop, containerBottom, containerLeading, containerTrailing])
84 |
85 | noDataView.addSubview(actionButton)
86 | noDataView.addSubview(noDataTextLabel)
87 |
88 |
89 | var allConstraints = [NSLayoutConstraint]()
90 |
91 | let buttonHorizontalConstraint = NSLayoutConstraint( item:noDataView,
92 | attribute:.centerX,
93 | relatedBy:.equal,
94 | toItem:actionButton,
95 | attribute:.centerX,
96 | multiplier:1,
97 | constant:0)
98 |
99 | let buttonVerticalConstraint = NSLayoutConstraint( item:actionButton,
100 | attribute:.top,
101 | relatedBy:.equal,
102 | toItem:noDataTextLabel,
103 | attribute:.bottom,
104 | multiplier:1,
105 | constant:20)
106 |
107 | let labelVerticalConstraint = NSLayoutConstraint( item:noDataView,
108 | attribute:.centerY,
109 | relatedBy:.equal,
110 | toItem:noDataTextLabel,
111 | attribute:.centerY,
112 | multiplier:1,
113 | constant:0)
114 |
115 | let labelHorizontalConstraint = NSLayoutConstraint( item:noDataView,
116 | attribute:.centerX,
117 | relatedBy:.equal,
118 | toItem:noDataTextLabel,
119 | attribute:.centerX,
120 | multiplier:1,
121 | constant:0)
122 |
123 | let labelWidthConstraint = NSLayoutConstraint(item: noDataTextLabel,
124 | attribute: .width,
125 | relatedBy: .lessThanOrEqual,
126 | toItem: nil,
127 | attribute: .notAnAttribute,
128 | multiplier: 1, constant: 300)
129 |
130 | allConstraints.append(labelVerticalConstraint)
131 | allConstraints.append(labelHorizontalConstraint)
132 | allConstraints.append(labelWidthConstraint)
133 | allConstraints.append(buttonHorizontalConstraint)
134 | allConstraints.append(buttonVerticalConstraint)
135 |
136 | noDataView.addConstraints(allConstraints)
137 |
138 | self.view.sendSubview(toBack: noDataView)
139 | }
140 | }
141 |
142 |
--------------------------------------------------------------------------------
/Podverse/UIViewController+AllowCellularDataDownloadsAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+AllowCellularDataDownloadsAlert.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 2/19/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIViewController {
12 | func showAllowCellularDataAlert(completion: ((Bool) -> ())? = nil) {
13 | UserDefaults.standard.set(true, forKey: kAskedToAllowCellularDataDownloads)
14 |
15 | let allowCellularDataAlert = UIAlertController(title: "Enable Cellular Data?", message: "Do you want to allow downloading episodes using cellular data when not connected to Wi-Fi?", preferredStyle: UIAlertControllerStyle.alert)
16 |
17 | allowCellularDataAlert.addAction(UIAlertAction(title: "Yes", style: .default) { (_) -> Void in
18 | UserDefaults.standard.set(true, forKey: kAllowCellularDataDownloads)
19 | completion?(true)
20 | })
21 |
22 | allowCellularDataAlert.addAction(UIAlertAction(title: "No", style: .cancel) { (_) -> Void in
23 | UserDefaults.standard.set(false, forKey: kAllowCellularDataDownloads)
24 | completion?(false)
25 | })
26 |
27 | DispatchQueue.main.async {
28 | self.present(allowCellularDataAlert, animated: true, completion: nil)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Podverse/UIViewController+InternetRequiredAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+InternetRequiredAlert.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/26/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIViewController {
12 | func showInternetNeededAlertWithDescription(message: String) {
13 | let internetNeededAlert = PVReachability.shared.createInternetConnectionNeededAlertWithDescription(message)
14 | DispatchQueue.main.async {
15 | self.present(internetNeededAlert, animated: true, completion: nil)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Podverse/UIViewController+ShowToast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+ShowToast.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 11/21/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // Thanks to Mr. Bean https://stackoverflow.com/a/35130932/2608858
12 | extension UIViewController {
13 |
14 | func showToast(message : String) {
15 |
16 | let toastLabel = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 90, y: self.view.frame.size.height/2 - 30, width: 180, height: 40))
17 | toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
18 | toastLabel.textColor = UIColor.white
19 | toastLabel.textAlignment = .center;
20 | toastLabel.font = UIFont(name: "System", size: 16.0)
21 | toastLabel.text = message
22 | toastLabel.alpha = 1.0
23 | toastLabel.layer.cornerRadius = 5;
24 | toastLabel.clipsToBounds = true
25 | self.view.addSubview(toastLabel)
26 | UIView.animate(withDuration: 3.0, delay: 0.1, options: .curveEaseOut, animations: {
27 | toastLabel.alpha = 0.0
28 | }, completion: {(isCompleted) in
29 | toastLabel.removeFromSuperview()
30 | })
31 |
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Podverse/URL+Converters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+Converters.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 8/1/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 |
13 | func convertToCustomUrl(scheme: String) -> URL? {
14 |
15 | guard var components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { return nil }
16 |
17 | if self.absoluteString.hasPrefix("http://") {
18 | components.scheme = "http" + scheme
19 | } else {
20 | components.scheme = "https" + scheme
21 | }
22 |
23 | return components.url
24 |
25 | }
26 |
27 | func convertToOriginalUrl() -> URL? {
28 |
29 | let actualUrlComponents = NSURLComponents(url: self, resolvingAgainstBaseURL: false)
30 |
31 | if self.scheme == "httpstreaming" {
32 | actualUrlComponents?.scheme = "http"
33 | } else if self.scheme == "httpsstreaming" {
34 | actualUrlComponents?.scheme = "https"
35 | }
36 |
37 | return actualUrlComponents?.url
38 |
39 | }
40 |
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/Podverse/URL+RemoteSize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+RemoteSize.swift
3 | // Podverse
4 | //
5 | // Created by Creon on 12/26/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 | var remoteSize: Int64 {
13 | var contentLength: Int64 = NSURLSessionTransferSizeUnknown
14 | var request = URLRequest(url: self, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData , timeoutInterval: 30.0);
15 |
16 | // I originally tried to derive expectedContentLength using httpMethod of "HEAD", but discovered that, while it worked for most podcsats, some nginx servers do not include Content Length in a response header.
17 |
18 | // Pod Save America is the podcast I discovered this issue with. Attempt to make a HEAD request with this link for an example https://rss.art19.com/episodes/92be9331-1cd4-4f93-88c7-c1f3139f2807.mp3
19 |
20 | // In order to work around this issue, I'm using a GET request instead, then overriding the NSURLSessionDataDelegate in PVStreamer to determine the content length, then immediately cancel the session for that GET request.
21 |
22 | request.httpMethod = "GET";
23 | request.timeoutInterval = 5;
24 | let group = DispatchGroup()
25 | group.enter()
26 |
27 | URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
28 | contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
29 | group.leave()
30 | }).resume()
31 | _ = group.wait(timeout: DispatchTime.now() + .seconds(5))
32 | return contentLength
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Podverse/URL+getQueryParamValue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+getQueryParamValue.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 7/31/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 | func getQueryParamValue(_ param:String) -> String? {
13 | guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true) else {
14 | return nil
15 | }
16 |
17 | return components.queryItems?.first(where: { $0.name == param})?.value
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Podverse/Utilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utilities.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 10/17/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | func checkForConnectivity() -> Bool {
13 |
14 | guard PVReachability.shared.hasInternetConnection() else {
15 | return false
16 | }
17 |
18 | return true
19 |
20 | }
21 |
22 | func checkForResults(results: [Any]?) -> Bool {
23 |
24 | guard let results = results, results.count > 0 else {
25 | return false
26 | }
27 |
28 | return true
29 |
30 | }
31 |
32 | func showNetworkActivityIndicator() {
33 | DispatchQueue.main.async {
34 | (UIApplication.shared.delegate as? AppDelegate)?.networkCounter += 1
35 | }
36 | }
37 |
38 | func hideNetworkActivityIndicator() {
39 | DispatchQueue.main.async {
40 | (UIApplication.shared.delegate as? AppDelegate)?.networkCounter -= 1
41 | }
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/Podverse/WebKitViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebKitViewController.swift
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 4/28/18.
6 | // Copyright © 2018 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import WebKit
11 |
12 | class WebKitViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
13 |
14 | var urlString: String?
15 |
16 | @IBOutlet weak var webView: WKWebView!
17 | @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 |
22 | self.webView.uiDelegate = self
23 |
24 | self.webView.navigationDelegate = self
25 |
26 | self.activityIndicatorView.hidesWhenStopped = true
27 |
28 | if let urlString = urlString, let url = URL(string: urlString) {
29 | showNetworkActivityIndicator()
30 | self.activityIndicatorView.startAnimating()
31 | let request = URLRequest(url: url)
32 | webView.load(request)
33 | }
34 | }
35 |
36 | // func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
37 | // showNetworkActivityIndicator()
38 | // self.activityIndicatorView.startAnimating()
39 | // }
40 |
41 |
42 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
43 | hideNetworkActivityIndicator()
44 | self.activityIndicatorView.stopAnimating()
45 | }
46 |
47 | func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
48 | hideNetworkActivityIndicator()
49 | self.activityIndicatorView.stopAnimating()
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/Podverse/podverse-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // podverse-Bridging-Header.h
3 | // Podverse
4 | //
5 | // Created by Mitchell Downey on 8/16/17.
6 | // Copyright © 2017 Podverse LLC. All rights reserved.
7 | //
8 |
9 | #ifndef podverse_Bridging_Header_h
10 |
11 | #import "StreamingKit.h"
12 |
13 | #define podverse_Bridging_Header_h
14 |
15 |
16 | #endif /* podverse_Bridging_Header_h */
17 |
--------------------------------------------------------------------------------
/Podverse/podverse.xcdatamodeld/podverse.xcdatamodel/contents:
--------------------------------------------------------------------------------
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 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/PodverseTests/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 |
--------------------------------------------------------------------------------
/PodverseTests/PodverseTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PodverseTests.swift
3 | // PodverseTests
4 | //
5 | // Created by Creon on 12/15/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class PodverseTests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | // Use XCTAssert and related functions to verify your tests produce the correct results.
26 | let myPodcastHasbeenPlayed = false;
27 |
28 | XCTAssertTrue(myPodcastHasbeenPlayed, "Podcast has not been played. Did you hit play?")
29 | }
30 |
31 | func testPerformanceExample() {
32 | // This is an example of a performance test case.
33 | self.measure {
34 | // Put the code you want to measure the time of here.
35 | }
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/PodverseUITests/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 |
--------------------------------------------------------------------------------
/PodverseUITests/PodverseUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PodverseUITests.swift
3 | // PodverseUITests
4 | //
5 | // Created by Creon on 12/15/16.
6 | // Copyright © 2016 Podverse LLC. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class PodverseUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 |
18 | // In UI tests it is usually best to stop immediately when a failure occurs.
19 | continueAfterFailure = false
20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
21 | XCUIApplication().launch()
22 |
23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testExample() {
32 | // Use recording to get started writing UI tests.
33 | // Use XCTAssert and related functions to verify your tests produce the correct results.
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # podverse-ios
2 | Podcast subscribing and clip sharing mobile app.
3 |
4 | ## Setup
5 |
6 | ### CocoaPods
7 |
8 | Dependencies are loaded using CocoaPods. After cloning the repo, you'll need to install dependencies by typing:
9 |
10 | `pod install`
11 |
12 | After install finishes, open the Podverse.xcworkspace in Xcode.
13 |
14 | ### Auth0
15 |
16 | Podverse uses Auth0 for user authentication. In order to run the app locally, you will need to sign up for an Auth0 account, then add to the Supporting Files directory a file named Auth0.plist, and add the following key/value pairs:
17 |
18 | ```
19 | ClientId: {YOUR_CLIENT_ID}
20 | Domain: {YOUR_DOMAIN}
21 | ```
22 |
23 | You should now be able to run the app, and login with your Auth0 app credentials.
24 |
25 | ### Server Data
26 |
27 | By default, clip and podcast search data is provided by podverse.fm. To change the source of this data, update the corresponding URL/s in the Constants.swift file:
28 |
29 | ```
30 | let LOCAL_DEV_URL = "http://localhost:8080/"
31 | let DEV_URL = ""
32 | let PROD_URL = ""
33 | let BASE_URL = PROD_URL
34 | ```
--------------------------------------------------------------------------------