Repository: adimango/insights-for-instagram
Branch: master
Commit: 9583abeac1a5
Files: 42
Total size: 137.4 KB
Directory structure:
gitextract_zbz5km2e/
├── .gitignore
├── .swiftlint.yml
├── .travis.yml
├── LICENSE
├── Podfile
├── README.md
├── fastlane/
│ ├── Appfile
│ ├── Fastfile
│ └── README.md
├── insights-for-instagram/
│ ├── Account/
│ │ ├── AccountViewController.swift
│ │ ├── AddAccountInteractor.swift
│ │ ├── AddAccountPresenter.swift
│ │ └── AddAccountViewController.swift
│ ├── App/
│ │ ├── AppConfiguration.swift
│ │ ├── AppDelegate.swift
│ │ ├── AppExtensions.swift
│ │ └── AppUserAccount.swift
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── chevron_location_.imageset/
│ │ │ └── Contents.json
│ │ ├── icons8-Male User Filled_30.imageset/
│ │ │ └── Contents.json
│ │ ├── line-chart-graph.imageset/
│ │ │ └── Contents.json
│ │ ├── placeHolder.imageset/
│ │ │ └── Contents.json
│ │ └── user-icon.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── Insights/
│ │ ├── InsightsInteractor.swift
│ │ ├── InsightsPresenter.swift
│ │ ├── InsightsViewController.swift
│ │ └── Views/
│ │ ├── InstagramMediaCollectionViewCell.swift
│ │ └── InstagramMediaSectionTableViewCell.swift
│ ├── LaunchScreen.storyboard
│ ├── Models/
│ │ └── InstagramMedia.swift
│ ├── Networking/
│ │ └── InstagramAPI.swift
│ └── Services/
│ └── DataService.swift
├── insights-for-instagram.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ └── xcshareddata/
│ └── xcschemes/
│ └── insights-for-instagram.xcscheme
├── insights-for-instagram.xcworkspace/
│ └── contents.xcworkspacedata
└── insights-for-instagramTests/
├── Info.plist
├── InstagramAPITests.swift
└── InstagramMediaTests.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Environments
.env
.env.default
Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
================================================
FILE: .swiftlint.yml
================================================
disabled_rules:
- trailing_whitespace
- line_length
- type_name
- identifier_name
opt_in_rules:
- empty_count
- force_unwrapping
excluded:
- Pods
function_body_length:
warning: 50
================================================
FILE: .travis.yml
================================================
language: objective-c
os: osx
osx_image: xcode9.2
xcode_workspace: insights-for-instagram.xcworkspace
podfile: Podfile
cache: cocoapods
notifications:
email: false
before_install:
- rvm install ruby --latest
- gem install cocoapods
- pod repo update master
- gem install fastlane --no-ri --no-rdoc --no-document
# - brew update
# - brew install swiftlint || true
jobs:
include:
- stage: XCTest
script:
- fastlane scan -s insights-for-instagram
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 alexdimango.me, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Podfile
================================================
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'insights-for-instagram' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
inhibit_all_warnings!
# Pods for insights-for-instagram
pod 'Moya'
pod 'RealmSwift'
pod 'ObjectMapper'
pod 'Kingfisher', '~> 4.0'
pod 'SwiftLint'
target 'insights-for-instagramTests' do
inherit! :search_paths
pod 'Quick'
pod 'Nimble'
end
end
================================================
FILE: README.md
================================================
# Insights for Instagram
A simple iOS Instagram's media insights App.


[](https://travis-ci.org/adimango/insights-for-instagram)

### Quick Start
Want to get the app running? Run this in your shell:
```sh
git clone https://github.com/adimango/insights-for-instagram.git
cd insights-for-instagram
pod install
open insights-for-instagram.xcworkspace
```
You will have a running version of the insights-for-instagram app by hitting `Build > Run`.
### Features
* Passwordless Reports, simply add your Instagram username, and start seeing reports right away
* Best Engagement, insights for Instagram gives you information about which posts resonate better than others
* Top Most Commented and Liked, the posts that generated the most comments or likes
### Planned features
* Best Day to Post, find out what your best time to post on Instagram is
* Hashtag Performance, discover which hashtags deliver the most engagement
### Questions
If you have questions about any aspect of this project, please feel free to
[open an issue](https://github.com/adimango/insights-for-instagram/issues/new).
### Credits
- [Moya][]: network abstraction layer written in Swift
- [Realm][]: data layer written in Swift
- [Quick][]: behavior-driven development framework for Swift
### License
MIT License. See [LICENSE](LICENSE).
[Moya]:https://github.com/Moya/Moya
[Realm]:https://realm.io/docs/swift/latest/
[Quick]:https://github.com/Quick/Quick
## Updates
### Apr 04, 2018: Banned private API
Instagram has banned getting data from `https://www.instagram.com/{username}/?__a=1`
With a bit of javascript we can still get the same json as before, but there are not workarounds yet for the get items queries.
```
// GET /api/users/:user_name -> returns user account details
// Below an example with National Geographic
https://insights-for-instagram.herokuapp.com/api/users/natgeo
```
### Dec 09, 2017: Back on Track
The app is using a new Instragram API proxy, developed using `/graphql/query` and some web API params.
```
// GET /api/users/:user_name/media -> returns media
// Below an example with National Geographic
https://insights-for-instagram.herokuapp.com/api/users/natgeo/media
```
### Dec 01, 2017: Breaking Changes
The Instragram API media endpoints now returns to 404-pages. After more of 20k downloads in just few month, Instagram removed the public API. However, the advanced queries are still available and a workaround will be push soon!
================================================
FILE: fastlane/Appfile
================================================
# The Appfile can be used to specify information that's used across all fastlane
# tools, like your username or the app's bundle identifier.
#
# For more details, check out the documentation at:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md
# app_identifier "me.alexdimango.insights-for-instagram"
# apple_id "alex.dimango@gmail.com"
================================================
FILE: fastlane/Fastfile
================================================
# More documentation about how to customize your build
# can be found here:
# https://docs.fastlane.tools
fastlane_version "1.109.0"
default_platform :ios
desc "Release a new beta version on Testflight"
desc "This action does the following:"
desc ""
desc "- Ensures a clean git status"
desc "- Increment the build number"
desc "- Build and sign the app"
desc "- Upload the ipa file to Testflight"
desc "- Commit and push the version bump"
lane :beta do
# make sure we start off with a clean slate
ensure_git_status_clean
# increment build number
increment_build_number
# increment to the specified version number
version = increment_version_number
# build your iOS app
gym(
# scheme: "insights-for-instagram",
export_method: "app-store"
)
# upload to Testflight
pilot(skip_waiting_for_build_processing: true)
# make sure our directory is clean, except for changes Fastlane has made
clean_build_artifacts
# tag release and push to GitHub
sh "git add .. ; git commit -m 'Deploying version #{version}.'"
add_git_tag tag: version
push_to_git_remote
end
error do |lane, exception|
if lane == :deploy
puts "Unable to deploy, resetting git repository."
clean_build_artifacts
reset_git_repo
end
end
================================================
FILE: fastlane/README.md
================================================
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
## Choose your installation method:
<table width="100%" >
<tr>
<th width="33%"><a href="http://brew.sh">Homebrew</a></td>
<th width="33%">Installer Script</td>
<th width="33%">Rubygems</td>
</tr>
<tr>
<td width="33%" align="center">macOS</td>
<td width="33%" align="center">macOS</td>
<td width="33%" align="center">macOS or Linux with Ruby 2.0.0 or above</td>
</tr>
<tr>
<td width="33%"><code>brew cask install fastlane</code></td>
<td width="33%"><a href="https://download.fastlane.tools">Download the zip file</a>. Then double click on the <code>install</code> script (or run it in a terminal window).</td>
<td width="33%"><code>sudo gem install fastlane -NV</code></td>
</tr>
</table>
# Available Actions
### beta
```
fastlane beta
```
Release a new beta version on Testflight
This action does the following:
- Ensures a clean git status
- Increment the build number
- Build and sign the app
- Upload the ipa file to Testflight
- Commit and push the version bump
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
================================================
FILE: insights-for-instagram/Account/AccountViewController.swift
================================================
import UIKit
class AccountViewController: UITableViewController {
// MARK: - Properties
@IBOutlet weak var accountNameTableCell: UITableViewCell!
// MARK: - View lifecycle
override func viewWillAppear(_ animated: Bool) {
tableView.tableFooterView = UIView()
guard let name = AppUserAccount().name else {
accountNameTableCell.textLabel?.text = NSLocalizedString("Account Name", comment: "")
return
}
accountNameTableCell.textLabel?.text = name
}
override func viewDidDisappear(_ animated: Bool) {
accountNameTableCell.isSelected = false
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row > 1 {
let cell = tableView.cellForRow(at: indexPath)
cell?.isSelected = false
diplayDeleteAllReportsAlert()
}
}
// MARK: - Actions
private func diplayDeleteAllReportsAlert() {
let alertController = UIAlertController(title: AppConfiguration.Messages.deleteReportsTitle, message: AppConfiguration.Messages.deleteReportsMessage, preferredStyle: UIAlertControllerStyle.alert)
let deleteAllAction = UIAlertAction(title: AppConfiguration.Messages.deleteAllButton, style: UIAlertActionStyle.destructive) { (_: UIAlertAction) -> Void in
self.accountNameTableCell.textLabel?.text = NSLocalizedString("Account Name", comment: "")
DataService.deleteAll()
}
let cancelAction = UIAlertAction(title: AppConfiguration.Messages.cancelButton, style: UIAlertActionStyle.default) { (_: UIAlertAction) -> Void in
}
alertController.addAction(cancelAction)
alertController.addAction(deleteAllAction)
present(alertController, animated: true, completion: nil)
}
}
================================================
FILE: insights-for-instagram/Account/AddAccountInteractor.swift
================================================
import UIKit
class AddAccountInteractor {
// MARK: - Properties
var presenter: AddAccountPresenter?
// MARK: - Storing/updating account logic
func validateAccount(with userName: String) {
presenter?.presentLoadingIndicator()
if AppUserAccount().name != userName { //remove old account data
DataService.deleteAll()
}
DataService.media(for: userName) { (error) in
guard let error = error else {
self.presenter?.presentReportsCompleted()
NotificationCenter.default.post(name: AppConfiguration.DefaultsNotifications.reload, object: nil)
return
}
self.stopLoading(with: error)
}
}
func loadAccount() {
guard let name = AppUserAccount().name else {
presenter?.presentAddAccount()
return
}
presenter?.presentUpdateAccount(account: name)
}
func deleteAccount() {
presenter?.presentAddAccount()
DataService.deleteAll()
}
private func stopLoading(with message: String) {
presenter?.presentAlertController(title: AppConfiguration.Messages.somethingWrongMessage, message: message)
}
}
================================================
FILE: insights-for-instagram/Account/AddAccountPresenter.swift
================================================
import Foundation
import UIKit
class AddAccountPresenter {
// MARK: - Properties
weak var viewController: AddAccountDisplayLogic?
// MARK: AddCoountPresentation Logic
func presentAddAccount() {
let leftBarButton = UIBarButtonItem()
leftBarButton.title = "Account"
let rightBarButton = UIBarButtonItem(title: "Done", style: .plain, target: viewController, action: #selector(AddAccountViewController.doneTapped))
self.viewController?.diplayAddAccount(with: leftBarButton, rightBarButton: rightBarButton)
}
func presentLoadingIndicator() {
let activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
activityIndicator.color = UIColor.black
activityIndicator.startAnimating()
let leftBarButton = UIBarButtonItem(title: "Cancel", style: .plain, target: viewController, action: #selector(AddAccountViewController.cancelTapped))
let rightBarButton = UIBarButtonItem(customView: activityIndicator)
viewController?.diplayLoadingAccount(with: leftBarButton, rightBarButton: rightBarButton)
}
func presentUpdateAccount(account: String) {
let leftBarButton = UIBarButtonItem()
leftBarButton.title = "Account"
let rightBarButton = UIBarButtonItem(title: "Done", style: .plain, target: viewController, action: #selector(AddAccountViewController.doneTapped))
viewController?.diplayUpdateAccount(with: account, leftBarButton: leftBarButton, rightBarButton: rightBarButton)
}
func presentReportsCompleted() {
presentAddAccount()
viewController?.diplayAlert(title: AppConfiguration.Messages.reportsCompletedTitle, message: AppConfiguration.Messages.reportsCompletedMessage)
}
// MARK: - Present Alert Controller
func presentAlertController(title: String, message: String) {
presentAddAccount()
viewController?.diplayAlert(title: title, message: message)
}
}
================================================
FILE: insights-for-instagram/Account/AddAccountViewController.swift
================================================
import UIKit
protocol AddAccountDisplayLogic: class {
func diplayAddAccount(with leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem)
func diplayLoadingAccount(with leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem)
func diplayUpdateAccount(with accountName: String, leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem)
func diplayAlert(title: String, message: String)
}
class AddAccountViewController: UIViewController, AddAccountDisplayLogic {
// MARK: - Properties
@IBOutlet weak var usernameTextfield: UITextField!
var interactor: AddAccountInteractor?
var presenter: AddAccountPresenter?
// MARK: Object lifecycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: Setup
private func setup() {
let viewController = self
let interactor = AddAccountInteractor()
let presenter = AddAccountPresenter()
viewController.interactor = interactor
viewController.presenter = presenter
interactor.presenter = presenter
presenter.viewController = viewController
}
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
usernameTextfield.becomeFirstResponder()
interactor?.loadAccount()
}
// MARK: - Actions
@objc func doneTapped() {
guard let username = usernameTextfield.text, !username.isEmpty else {
return
}
usernameTextfield.resignFirstResponder()
interactor?.validateAccount(with: username)
}
@objc func cancelTapped() {
usernameTextfield.text = ""
interactor?.deleteAccount()
}
// MARK: - AddAccountDisplayLogic
func diplayAddAccount(with leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem) {
usernameTextfield.isEnabled = true
navigationItem.hidesBackButton = false
navigationItem.setLeftBarButton(nil, animated: true)
navigationItem.setRightBarButton(rightBarButton, animated: true)
}
func diplayLoadingAccount(with leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem) {
usernameTextfield.isEnabled = false
navigationItem.setLeftBarButton(leftBarButton, animated: true)
navigationItem.setRightBarButton(rightBarButton, animated: true)
}
func diplayUpdateAccount(with accountName: String, leftBarButton: UIBarButtonItem, rightBarButton: UIBarButtonItem) {
usernameTextfield.text = accountName
usernameTextfield.isEnabled = true
usernameTextfield.becomeFirstResponder()
navigationItem.hidesBackButton = false
navigationItem.setLeftBarButton(nil, animated: true)
navigationItem.setRightBarButton(rightBarButton, animated: true)
}
func diplayAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let alertAction = UIAlertAction(title: AppConfiguration.Messages.okButton, style: .default, handler: nil)
alertController.addAction(alertAction)
present(alertController, animated: true, completion: nil)
}
}
================================================
FILE: insights-for-instagram/App/AppConfiguration.swift
================================================
import Foundation
class AppConfiguration {
// MARK: - AppConfiguration.TableViewSection
struct TableViewSections {
static let zero = NSLocalizedString("Best Engagement", comment: "")
static let one = NSLocalizedString("Top 25 Most Commented", comment: "")
static let two = NSLocalizedString("Recently Posted", comment: "")
}
// MARK: - AppConfiguration.TableViewCellIdentifiers
struct TableViewCellIdentifiers {
static let cell = "cellID"
}
struct DefaultsNotifications {
static let reload = Notification.Name(rawValue: "reload")
}
// MARK: - AppConfiguration.Messages
struct Messages {
static let okButton = NSLocalizedString("OK", comment: "")
static let doneButton = NSLocalizedString("Done", comment: "")
static let cancelButton = NSLocalizedString("Cancel", comment: "")
static let deleteAllButton = NSLocalizedString("Delete All", comment: "")
static let somethingWrongMessage = NSLocalizedString("Something went wrong", comment: "")
static let privateAccountMessage = NSLocalizedString("This account is private", comment: "")
static let reportsCompletedTitle = NSLocalizedString("Reports Completed", comment: "")
static let reportsCompletedMessage = NSLocalizedString("Please go back to the Insights screen", comment: "")
static let deleteReportsTitle = NSLocalizedString("Delete Reports", comment: "")
static let deleteReportsMessage = NSLocalizedString("Remove my account and delete all the reports", comment: "")
}
}
================================================
FILE: insights-for-instagram/App/AppDelegate.swift
================================================
import UIKit
import RealmSwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
setupAppearance()
return true
}
// MARK: Setup UIAppearance
private func setupAppearance() {
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().tintColor = UIColor.red
UITabBar.appearance().tintColor = UIColor.init(red: 233/255, green: 79/255, blue: 97/255, alpha: 1)
}
}
================================================
FILE: insights-for-instagram/App/AppExtensions.swift
================================================
import UIKit
// MARK: - Formatter
extension Formatter {
static let withPoint: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = "."
formatter.numberStyle = .decimal
return formatter
}()
}
// MARK: - Integer
extension BinaryInteger {
var formattedWithPoint: String {
return Formatter.withPoint.string(for: self) ?? ""
}
}
// MARK: - String
extension String {
/// -returns: A string without white spaces and new lines.
func trim() -> String {
return self.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
================================================
FILE: insights-for-instagram/App/AppUserAccount.swift
================================================
import Foundation
class AppUserAccount {
enum DefaultsKeys: String {
case UserAccountKey
}
// MARK: - Initializers
let defaults: UserDefaults
init(defaults: UserDefaults) {
self.defaults = defaults
}
init() {
self.defaults = UserDefaults.standard
}
// MARK: - Properties
var name: String? {
get {
let key = defaults.string(forKey: DefaultsKeys.UserAccountKey.rawValue)
return key
}
set(newName) {
guard let name = newName else {
defaults.removeObject(forKey: DefaultsKeys.UserAccountKey.rawValue)
defaults.synchronize()
return
}
defaults.set(name, forKey: DefaultsKeys.UserAccountKey.rawValue)
defaults.synchronize()
}
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x-1.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "insights-29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "insights-58.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "insights-87.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon-App-57x57@1x.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon-App-57x57@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x-2.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50x50@1x.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50x50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-App-72x72@1x.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "Icon-App-72x72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "iTunesArtwork@2x.png",
"scale" : "1x"
},
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-App-29x29@2x.png",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-App-29x29@3x.png",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"filename" : "Icon-86@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"filename" : "Icon-98@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
},
{
"idiom" : "watch-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/chevron_location_.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "chevron_location_@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "chevron_location_@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/icons8-Male User Filled_30.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "icons8-Male User Filled_30.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icons8-Male User Filled_60.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/line-chart-graph.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "icons8-Line Chart_30.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icons8-Line Chart_60.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/placeHolder.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "test.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Assets.xcassets/user-icon.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "icons8-Male User Filled-50.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icons8-Male User Filled-100.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: insights-for-instagram/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
<device id="retina4_0" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Insights View Controller-->
<scene sceneID="RRM-QO-QT9">
<objects>
<tableViewController id="Ch5-O9-Ye5" customClass="InsightsViewController" customModule="insights_for_instagram" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="200" sectionHeaderHeight="28" sectionFooterHeight="28" id="QCx-Gf-Hye">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="OKS-hm-khu" userLabel="Header">
<rect key="frame" x="0.0" y="0.0" width="320" height="83"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Insights" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ddA-8q-D8E">
<rect key="frame" x="20" y="23" width="156.5" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="DUu-BP-hYw"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="40"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4dO-Iu-9Ez">
<rect key="frame" x="260" y="28" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="2v9-0o-9Wl"/>
<constraint firstAttribute="height" constant="40" id="4pX-CE-YYD"/>
</constraints>
<state key="normal" image="user-icon"/>
<connections>
<action selector="displayAccount:" destination="Ch5-O9-Ye5" eventType="touchUpInside" id="JXk-Tk-puz"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="4dO-Iu-9Ez" secondAttribute="trailing" constant="20" id="65d-cu-JAI"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ddA-8q-D8E" secondAttribute="trailing" constant="20" symbolic="YES" id="7lQ-6q-K8u"/>
<constraint firstAttribute="bottom" secondItem="ddA-8q-D8E" secondAttribute="bottom" constant="10" id="L8Z-mG-mNm"/>
<constraint firstItem="ddA-8q-D8E" firstAttribute="leading" secondItem="OKS-hm-khu" secondAttribute="leading" constant="20" id="d7n-qM-9fa"/>
<constraint firstAttribute="bottom" secondItem="4dO-Iu-9Ez" secondAttribute="bottom" constant="15" id="dw0-46-TdA"/>
</constraints>
</view>
<view key="tableFooterView" contentMode="scaleToFill" id="AuA-z4-vXQ" userLabel="Footer">
<rect key="frame" x="0.0" y="83" width="320" height="200"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JH6-hT-hFq" userLabel="WeekdayBlue">
<rect key="frame" x="20" y="46" width="279" height="133"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Weekday." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Mma-WH-dqA" userLabel="WeekdayLabel">
<rect key="frame" x="46" y="41" width="188" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="47T-IG-GLM"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="40"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Mma-WH-dqA" firstAttribute="centerX" secondItem="JH6-hT-hFq" secondAttribute="centerX" id="WZL-cB-1bV"/>
<constraint firstItem="Mma-WH-dqA" firstAttribute="centerY" secondItem="JH6-hT-hFq" secondAttribute="centerY" id="Xgt-Ut-SW3"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your Best Day to Post" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q6i-Ho-9L4" userLabel="WeekdayTitleLabel">
<rect key="frame" x="20" y="8" width="279" height="27"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="27" id="M9R-QC-kkq"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="18"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view alpha="0.40000000000000002" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="S9g-N7-BEs" userLabel="Separator">
<rect key="frame" x="20" y="0.0" width="300" height="0.5"/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="lq3-8e-BJQ"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="JH6-hT-hFq" secondAttribute="bottom" constant="21" id="Bhh-7U-67L"/>
<constraint firstAttribute="trailing" secondItem="S9g-N7-BEs" secondAttribute="trailing" id="FYt-nZ-XGM"/>
<constraint firstItem="S9g-N7-BEs" firstAttribute="leading" secondItem="AuA-z4-vXQ" secondAttribute="leading" constant="20" id="Ibs-wR-T8L"/>
<constraint firstItem="Q6i-Ho-9L4" firstAttribute="trailing" secondItem="JH6-hT-hFq" secondAttribute="trailing" id="OLf-2n-C1s"/>
<constraint firstAttribute="trailing" secondItem="JH6-hT-hFq" secondAttribute="trailing" constant="21" id="PNb-BN-QxD"/>
<constraint firstItem="Q6i-Ho-9L4" firstAttribute="top" secondItem="AuA-z4-vXQ" secondAttribute="top" constant="8" id="R3n-VW-hIT"/>
<constraint firstItem="JH6-hT-hFq" firstAttribute="top" secondItem="AuA-z4-vXQ" secondAttribute="top" constant="46" id="UcS-Q1-QVu"/>
<constraint firstItem="JH6-hT-hFq" firstAttribute="leading" secondItem="AuA-z4-vXQ" secondAttribute="leading" constant="20" id="ere-lG-9lu"/>
<constraint firstItem="Q6i-Ho-9L4" firstAttribute="leading" secondItem="JH6-hT-hFq" secondAttribute="leading" id="ln3-WV-4c0"/>
<constraint firstItem="Q6i-Ho-9L4" firstAttribute="leading" secondItem="AuA-z4-vXQ" secondAttribute="leading" constant="20" id="pCv-yf-KPE"/>
<constraint firstItem="S9g-N7-BEs" firstAttribute="top" secondItem="AuA-z4-vXQ" secondAttribute="top" id="rXb-fF-3ml"/>
</constraints>
</view>
<sections/>
<connections>
<outlet property="dataSource" destination="Ch5-O9-Ye5" id="P7C-ZE-IvU"/>
<outlet property="delegate" destination="Ch5-O9-Ye5" id="CiD-gA-jJv"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="VVd-PE-ePJ"/>
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="S7E-Wr-Of8">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
<connections>
<action selector="refresh:" destination="Ch5-O9-Ye5" eventType="primaryActionTriggered" id="1vI-Tk-p4W"/>
</connections>
</refreshControl>
<connections>
<outlet property="footerView" destination="AuA-z4-vXQ" id="shM-JA-JeO"/>
<outlet property="refreshController" destination="S7E-Wr-Of8" id="Vz3-bD-Fp4"/>
<outlet property="weekdayLabel" destination="Mma-WH-dqA" id="NZp-03-SZ0"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="IHe-Uq-TEh" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2010" y="-639.08450704225356"/>
</scene>
<!--Tab Bar Controller-->
<scene sceneID="yl2-sM-qoP">
<objects>
<tabBarController id="49e-Tb-3d3" sceneMemberID="viewController">
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="W28-zg-YXA">
<rect key="frame" x="0.0" y="975" width="768" height="49"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tabBar>
<connections>
<segue destination="S7o-FW-vkP" kind="relationship" relationship="viewControllers" id="L1r-x4-rcR"/>
<segue destination="ysz-7x-ApL" kind="relationship" relationship="viewControllers" id="lzU-1b-eKA"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="161" y="-638"/>
</scene>
<!--Insights-->
<scene sceneID="s73-Ab-IC3">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" navigationBarHidden="YES" id="S7o-FW-vkP" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Insights" image="line-chart-graph" id="8FS-IR-PId"/>
<toolbarItems/>
<nil key="simulatedTopBarMetrics"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="M8X-1l-2gm">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="Ch5-O9-Ye5" kind="relationship" relationship="rootViewController" id="gwH-ca-ERB"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7Le-HY-pMD" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1087" y="-637"/>
</scene>
<!--Account-->
<scene sceneID="eMh-5G-Nq0">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ysz-7x-ApL" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Account" image="icons8-Male User Filled_30" id="cPa-gy-q4n"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="T60-t6-wuk">
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="NuY-RT-tWP" kind="relationship" relationship="rootViewController" id="JzL-oD-EPK"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="i1Z-ND-hRm" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1086" y="171"/>
</scene>
<!--Account-->
<scene sceneID="5Fj-1g-qAM">
<objects>
<tableViewController id="NuY-RT-tWP" customClass="AccountViewController" customModule="insights_for_instagram" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="piD-qk-mqj">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.98823529409999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="separatorInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
<color key="sectionIndexBackgroundColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.98823529409999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<sections>
<tableViewSection id="8sB-ME-b0N">
<cells>
<tableViewCell clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="15" id="6lW-Ye-PYw">
<rect key="frame" x="0.0" y="0.0" width="320" height="15"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6lW-Ye-PYw" id="19o-yW-okD">
<rect key="frame" x="0.0" y="0.0" width="320" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
<color key="backgroundColor" red="0.97254901960784312" green="0.97254901960784312" blue="0.9882352941176471" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="2I4-Ml-xRV" imageView="wvX-I4-BAE" rowHeight="70" style="IBUITableViewCellStyleDefault" id="nGc-Qn-QV3">
<rect key="frame" x="0.0" y="15" width="320" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nGc-Qn-QV3" id="923-u0-cbw">
<rect key="frame" x="0.0" y="0.0" width="286" height="69.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Account name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2I4-Ml-xRV">
<rect key="frame" x="81" y="0.0" width="204" height="69.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="user-icon" id="wvX-I4-BAE">
<rect key="frame" x="16" y="9" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="lSo-z7-nYe" kind="show" id="lzZ-Rq-ean"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="40" id="gV3-y1-XvB">
<rect key="frame" x="0.0" y="85" width="320" height="40"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="gV3-y1-XvB" id="ahF-rx-rsn">
<rect key="frame" x="0.0" y="0.0" width="320" height="39.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
<color key="backgroundColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.98823529409999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="yVk-qv-tLH" imageView="FTJ-pB-1od" rowHeight="50" style="IBUITableViewCellStyleDefault" id="wf2-5F-5vv">
<rect key="frame" x="0.0" y="125" width="320" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wf2-5F-5vv" id="otO-vZ-lXh">
<rect key="frame" x="0.0" y="0.0" width="320" height="49.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Delete my reports" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="yVk-qv-tLH">
<rect key="frame" x="15" y="0.0" width="289" height="49.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="FTJ-pB-1od">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="NuY-RT-tWP" id="36h-ze-7K9"/>
<outlet property="delegate" destination="NuY-RT-tWP" id="EDe-34-Te7"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Account" id="34M-Ra-SjB"/>
<connections>
<outlet property="accountNameTableCell" destination="nGc-Qn-QV3" id="fVJ-9C-8gO"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PcN-AU-9Kl" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2010" y="170"/>
</scene>
<!--Account-->
<scene sceneID="PhT-p2-mfK">
<objects>
<viewController title="Account" hidesBottomBarWhenPushed="YES" id="lSo-z7-nYe" customClass="AddAccountViewController" customModule="insights_for_instagram" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="fsY-Ho-ipb"/>
<viewControllerLayoutGuide type="bottom" id="eQH-5h-mds"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="wpc-8T-JO0">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G9F-RN-IqA">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Insights are only available for public instagram accounts. You'll see statistics for your last thousand posts." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zHl-Nw-eLb">
<rect key="frame" x="8" y="18" width="304" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="RP1-Hz-CiQ"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.59607843137254901" green="0.59607843137254901" blue="0.62352941176470589" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Hrj-dS-INX" userLabel="Container">
<rect key="frame" x="0.0" y="70" width="320" height="70"/>
<subviews>
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="T1q-qH-NoW" userLabel="Separator">
<rect key="frame" x="0.0" y="0.0" width="320" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.67000000000000004" id="eUO-Ja-bKH"/>
</constraints>
</view>
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="was-CB-ySX" userLabel="Separator">
<rect key="frame" x="0.0" y="69.5" width="320" height="0.5"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="0.67000000000000004" id="5xQ-FI-5Kh"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="0.67000000000000004" id="er4-CN-Ufa"/>
</constraints>
</view>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="cRH-vl-inv">
<rect key="frame" x="8" y="10" width="304" height="50"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="lSo-z7-nYe" id="9SZ-4E-fbG"/>
</connections>
</textField>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="was-CB-ySX" secondAttribute="trailing" id="02Y-AV-EWR"/>
<constraint firstAttribute="bottom" secondItem="was-CB-ySX" secondAttribute="bottom" id="7RU-kg-BSa"/>
<constraint firstAttribute="trailing" secondItem="T1q-qH-NoW" secondAttribute="trailing" id="GwU-aP-Oyi"/>
<constraint firstItem="T1q-qH-NoW" firstAttribute="top" secondItem="Hrj-dS-INX" secondAttribute="top" id="Hem-xe-j0g"/>
<constraint firstItem="cRH-vl-inv" firstAttribute="leading" secondItem="Hrj-dS-INX" secondAttribute="leading" constant="8" id="Mb7-aw-qe7"/>
<constraint firstItem="was-CB-ySX" firstAttribute="leading" secondItem="Hrj-dS-INX" secondAttribute="leading" id="O5L-fS-fc6"/>
<constraint firstItem="was-CB-ySX" firstAttribute="top" secondItem="cRH-vl-inv" secondAttribute="bottom" constant="9.3300000000000001" id="T4c-0G-DVn"/>
<constraint firstAttribute="trailing" secondItem="cRH-vl-inv" secondAttribute="trailing" constant="8" id="TwE-gx-RyZ"/>
<constraint firstItem="T1q-qH-NoW" firstAttribute="leading" secondItem="Hrj-dS-INX" secondAttribute="leading" id="g61-IM-4qz"/>
<constraint firstItem="cRH-vl-inv" firstAttribute="top" secondItem="T1q-qH-NoW" secondAttribute="bottom" constant="9.3300000000000001" id="hGp-Lz-oy0"/>
<constraint firstAttribute="height" constant="70" id="hqG-ju-q1U"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="The report generation only takes a few minutes. But it depends from the internet connection speed." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2ja-Y4-tBv">
<rect key="frame" x="12" y="148" width="296" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="1R8-7Q-bZa"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="40" id="2Ev-HN-qB7"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.59607843137254901" green="0.59607843137254901" blue="0.62352941176470589" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.98823529409999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Hrj-dS-INX" firstAttribute="top" secondItem="zHl-Nw-eLb" secondAttribute="bottom" constant="2" id="BJo-y5-LoF"/>
<constraint firstAttribute="trailing" secondItem="Hrj-dS-INX" secondAttribute="trailing" id="HhD-i5-lAj"/>
<constraint firstAttribute="bottom" secondItem="zHl-Nw-eLb" secondAttribute="bottom" constant="555" id="JAR-bl-3mo"/>
<constraint firstAttribute="trailing" secondItem="2ja-Y4-tBv" secondAttribute="trailing" constant="12" id="RdX-gE-BEs"/>
<constraint firstItem="zHl-Nw-eLb" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="G9F-RN-IqA" secondAttribute="leadingMargin" id="T8u-NC-ioa"/>
<constraint firstItem="2ja-Y4-tBv" firstAttribute="top" secondItem="Hrj-dS-INX" secondAttribute="bottom" constant="8" id="Whr-RV-8qw"/>
<constraint firstItem="zHl-Nw-eLb" firstAttribute="trailing" secondItem="G9F-RN-IqA" secondAttribute="trailingMargin" id="aK2-Hz-VbZ"/>
<constraint firstItem="Hrj-dS-INX" firstAttribute="leading" secondItem="G9F-RN-IqA" secondAttribute="leading" id="b7A-mK-aJA"/>
<constraint firstItem="2ja-Y4-tBv" firstAttribute="leading" secondItem="G9F-RN-IqA" secondAttribute="leading" constant="12" id="bBZ-Ql-9tc"/>
<constraint firstItem="zHl-Nw-eLb" firstAttribute="centerX" secondItem="G9F-RN-IqA" secondAttribute="centerX" id="bqT-wz-Lza"/>
<constraint firstItem="zHl-Nw-eLb" firstAttribute="top" secondItem="G9F-RN-IqA" secondAttribute="top" constant="18" id="iUN-U4-sAx"/>
</constraints>
</scrollView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="G9F-RN-IqA" secondAttribute="trailing" id="BGH-3j-Kad"/>
<constraint firstItem="eQH-5h-mds" firstAttribute="top" secondItem="G9F-RN-IqA" secondAttribute="bottom" id="ESM-Qp-UsE"/>
<constraint firstItem="G9F-RN-IqA" firstAttribute="top" secondItem="fsY-Ho-ipb" secondAttribute="bottom" id="Wm1-Dd-VQf"/>
<constraint firstItem="G9F-RN-IqA" firstAttribute="leading" secondItem="wpc-8T-JO0" secondAttribute="leading" id="l52-P5-v8X"/>
</constraints>
</view>
<nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="usernameTextfield" destination="cRH-vl-inv" id="6Gc-zi-eme"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="LvL-iT-gtN" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2854" y="170"/>
</scene>
</scenes>
<resources>
<image name="icons8-Male User Filled_30" width="30" height="30"/>
<image name="line-chart-graph" width="30" height="30"/>
<image name="user-icon" width="50" height="50"/>
</resources>
</document>
================================================
FILE: insights-for-instagram/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Insights</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleVersion</key>
<string>10</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: insights-for-instagram/Insights/InsightsInteractor.swift
================================================
import UIKit
class InsightsInteractor {
// MARK: - Properties
var presenter: InstagramMediaPresentation?
// MARK: Object lifecycle
init() {
NotificationCenter.default.addObserver(self, selector: #selector(InsightsInteractor.loadMedia), name: AppConfiguration.DefaultsNotifications.reload, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: - Load media
@objc func loadMedia() {
guard let userName = AppUserAccount().name else {
loadEmptyMedia()
return
}
loadStoredMedia()
DataService.media(for: userName) { (error) in
if error == nil {
self.loadStoredMedia()
}
}
}
func loadEmptyMedia() {
presenter?.presentNoAccountSections()
}
func loadStoredMedia() {
let bestEngagement = DataService.bestEngagement(with: 25)
let lastWeeksPosted = DataService.lastWeeksPosted(weeks: 12)
let topMostCommented = DataService.mostLiked(with: 25)
let bestEngagementDictionary: [String: Any] = ["sectionTitle": AppConfiguration.TableViewSections.zero, "items": bestEngagement]
let mostCommentedDictionary: [String: Any] = ["sectionTitle": AppConfiguration.TableViewSections.one, "items": topMostCommented]
let lastWeeksPostedDictionary: [String: Any] = ["sectionTitle": AppConfiguration.TableViewSections.two, "items": lastWeeksPosted]
presenter?.presentLoadedSections(with: [bestEngagementDictionary, mostCommentedDictionary, lastWeeksPostedDictionary])
}
func loadFetchMediaFailureAlert(error: Error) {
presenter?.presentAlertController(with: error.localizedDescription)
}
}
================================================
FILE: insights-for-instagram/Insights/InsightsPresenter.swift
================================================
import Foundation
import UIKit
struct InstagramMediaSection {
let sectionTitle: String
let instagramMediaViews: [InstagramMediaView]
}
struct InstagramMediaView {
let likes: String
let comments: String
let imageURL: String
}
protocol InstagramMediaPresentation {
func presentLoadedSections (with items: [[String: Any]])
func presentNoAccountSections ()
func presentAlertController(with message: String)
}
class InsightsPresenter: InstagramMediaPresentation {
// MARK: - Properties
weak var viewController: InsightsViewDisplayLogic?
// MARK: - Present fetched media
func presentLoadedSections(with items: [[String: Any]]) {
var instagramMediaSections = [InstagramMediaSection]()
for section in items {
guard let sectionTitle = section["sectionTitle"] as? String,
let instagramMedias = section["items"] as? [InstagramMedia] else { return }
var instagramMediaViews = [InstagramMediaView]()
for item in instagramMedias {
let itemView = InstagramMediaView(likes: NSLocalizedString("Likes: ", comment: "")+"\(item.likesCount.formattedWithPoint)", comments: NSLocalizedString("Comments: ", comment: "")+"\(item.commentsCount.formattedWithPoint)", imageURL: item.imageUrl)
instagramMediaViews.append(itemView)
}
let instagramMediaSection = InstagramMediaSection(sectionTitle: sectionTitle, instagramMediaViews: instagramMediaViews)
instagramMediaSections.append(instagramMediaSection)
}
let weekday = DataService.weekday()
viewController?.diplayFetchedMedia(instagramMediaSections: instagramMediaSections, weekday: weekday)
}
// MARK: - Present no account UI
func presentNoAccountSections() {
let instagramMediaSections = createPlaceHolderSection()
viewController?.diplayFetchedMedia(instagramMediaSections: instagramMediaSections, weekday: NSLocalizedString("Weekday.", comment: ""))
}
private func createPlaceHolderSection() -> [InstagramMediaSection] {
let item = InstagramMediaView(likes: NSLocalizedString("Likes", comment: ""), comments: NSLocalizedString("Comments", comment: ""), imageURL: "")
let items = Array(repeating: item, count: 3)
let instagramItemsSection_0 = InstagramMediaSection(sectionTitle: AppConfiguration.TableViewSections.zero, instagramMediaViews: items)
let instagramItemsSection_1 = InstagramMediaSection(sectionTitle: AppConfiguration.TableViewSections.one, instagramMediaViews: items)
let instagramItemsSection_2 = InstagramMediaSection(sectionTitle: AppConfiguration.TableViewSections.two, instagramMediaViews: items)
return [instagramItemsSection_0, instagramItemsSection_1, instagramItemsSection_2]
}
// MARK: - Present Alert Controller
func presentAlertController(with message: String) {
viewController?.diplayFetchMediaFailureAlert(title: AppConfiguration.Messages.somethingWrongMessage, message: message)
}
}
================================================
FILE: insights-for-instagram/Insights/InsightsViewController.swift
================================================
import UIKit
protocol InsightsViewDisplayLogic: class {
func diplayFetchedMedia(instagramMediaSections: [InstagramMediaSection], weekday: String)
func diplayFetchMediaFailureAlert(title: String, message: String)
}
class InsightsViewController: UITableViewController, InsightsViewDisplayLogic {
// MARK: - Properties
var interactor: InsightsInteractor?
var presenter: InstagramMediaPresentation?
var sections: [InstagramMediaSection]?
var storedOffsets = [Int: CGFloat]()
@IBOutlet weak var footerView: UIView?
@IBOutlet weak var weekdayLabel: UILabel!
@IBOutlet weak var refreshController: UIRefreshControl!
// MARK: Object lifecycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: Setup
private func setup() {
let viewController = self
let interactor = InsightsInteractor()
let presenter = InsightsPresenter()
viewController.interactor = interactor
viewController.presenter = presenter
interactor.presenter = presenter
presenter.viewController = viewController
sections = []
}
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
fetchMediaOnload()
}
// MARK: - SetupUI
private func setupUI() {
tableView.register(InstagramMediaSectionTableViewCell.self, forCellReuseIdentifier: AppConfiguration.TableViewCellIdentifiers.cell)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
tableView.separatorStyle = .none
self.footerView?.isHidden = true
}
// MARK: - Fetch Media
func fetchMediaOnload() {
DispatchQueue.global(qos: .background).async {
self.interactor?.loadMedia()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = sections else { return 0 }
return sections.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: AppConfiguration.TableViewCellIdentifiers.cell, for: indexPath) as? InstagramMediaSectionTableViewCell else {
return InstagramMediaSectionTableViewCell()
}
let instagramItemsSection = self.sections?[indexPath.row]
cell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
cell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
cell.sectionNameLabel.text = instagramItemsSection?.sectionTitle
return cell
}
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? InstagramMediaSectionTableViewCell else { return }
storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset
}
// MARK: - Actions
@IBAction func refresh(_ sender: Any) {
fetchMediaOnload()
}
@IBAction func displayAccount(_ sender: Any) {
if let tabBarController = UIApplication.shared.keyWindow?.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 1
}
}
func diplayFetchedMedia(instagramMediaSections: [InstagramMediaSection], weekday: String) {
self.sections = instagramMediaSections
DispatchQueue.main.async {
self.footerView?.isHidden = false
self.weekdayLabel.text = weekday
self.refreshController.endRefreshing()
self.tableView.reloadData()
}
}
func diplayFetchMediaFailureAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let alertAction = UIAlertAction(title: AppConfiguration.Messages.okButton, style: .default, handler: nil)
alertController.addAction(alertAction)
self.present(alertController, animated: true, completion: nil)
}
}
extension InsightsViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: - Collections view data source
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let index = collectionView.tag
let instagramMediaSection = self.sections?[index]
return (instagramMediaSection?.instagramMediaViews.count) ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let index = collectionView.tag
let instagramMediaSection = self.sections?[index]
let media = instagramMediaSection?.instagramMediaViews[indexPath.row]
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AppConfiguration.TableViewCellIdentifiers.cell, for: indexPath) as?
InstagramMediaCollectionViewCell else { return InstagramMediaCollectionViewCell() }
cell.mediaModelView = media
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let h = collectionView.frame.height - 10
return CGSize(width: 324/2, height: h)
}
}
================================================
FILE: insights-for-instagram/Insights/Views/InstagramMediaCollectionViewCell.swift
================================================
import UIKit
import Kingfisher
class InstagramMediaCollectionViewCell: UICollectionViewCell {
// MARK: - Properties
var mediaModelView: InstagramMediaView? {
didSet {
updateViews()
}
}
let imageView: UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 4
image.layer.masksToBounds = true
image.image = UIImage(named: "placeHolder")
return image
}()
let likesLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.regular)
return label
}()
let commentsLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.regular)
label.textColor = UIColor.lightGray
return label
}()
// MARK: Object lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
addSubview(imageView)
addSubview(likesLabel)
addSubview(commentsLabel)
imageView.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.width)
likesLabel.frame = CGRect(x: 0, y: frame.width + 6, width: frame.width, height: 16)
commentsLabel.frame = CGRect(x: 0, y: frame.width + 25, width: frame.width, height: 16)
}
func updateViews() {
self.likesLabel.text = mediaModelView?.likes
self.commentsLabel.text = mediaModelView?.comments
if let imageUrl = mediaModelView?.imageURL, let url = URL(string: imageUrl) {
self.imageView.kf.setImage(with: url)
}
}
override func prepareForReuse() {
imageView.image = UIImage(named: "placeHolder")
}
}
================================================
FILE: insights-for-instagram/Insights/Views/InstagramMediaSectionTableViewCell.swift
================================================
import Foundation
import UIKit
class InstagramMediaSectionTableViewCell: UITableViewCell {
// MARK: Object lifecycle
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Properties
let itemsCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 10)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = UIColor.clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
let sectionNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 18, weight: UIFont.Weight.heavy)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.4, alpha: 0.4)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
func setupView() {
contentView.backgroundColor = UIColor.clear
addSubview(itemsCollectionView)
addSubview(dividerLineView)
addSubview(sectionNameLabel)
itemsCollectionView.addConstraint(NSLayoutConstraint(item: itemsCollectionView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: (420/2)))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[sectionNameLabel]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["sectionNameLabel": sectionNameLabel]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[dividerLineView]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["dividerLineView": dividerLineView]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[itemsCollectionView]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["itemsCollectionView": itemsCollectionView]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[dividerLineView(0.5)][sectionNameLabel(40)][itemsCollectionView]-20-|", options: NSLayoutFormatOptions(), metrics: nil, views: ["itemsCollectionView": itemsCollectionView, "dividerLineView": dividerLineView, "sectionNameLabel": sectionNameLabel]))
itemsCollectionView.register(InstagramMediaCollectionViewCell.self, forCellWithReuseIdentifier: AppConfiguration.TableViewCellIdentifiers.cell)
}
}
// MARK: UICollectionViewDataSource
extension InstagramMediaSectionTableViewCell {
//Following https://github.com/ashfurrow/ design
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
itemsCollectionView.delegate = dataSourceDelegate
itemsCollectionView.dataSource = dataSourceDelegate
itemsCollectionView.tag = row
itemsCollectionView.setContentOffset(itemsCollectionView.contentOffset, animated: false)
itemsCollectionView.reloadData()
}
var collectionViewOffset: CGFloat {
set { itemsCollectionView.contentOffset.x = newValue }
get { return itemsCollectionView.contentOffset.x }
}
}
================================================
FILE: insights-for-instagram/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: insights-for-instagram/Models/InstagramMedia.swift
================================================
import ObjectMapper
import RealmSwift
class InstagramMedia: Object, Mappable {
@objc dynamic var id = ""
@objc dynamic var code = ""
@objc dynamic var type = ""
@objc dynamic var imageUrl = ""
@objc dynamic var createdTime = Date()
@objc dynamic var weekday = 0 // https://developer.apple.com/documentation/foundation/nsdatecomponents/1410442-weekday
@objc dynamic var likesCount = 0
@objc dynamic var commentsCount = 0
@objc dynamic var engagementCount = 0 // includes the total number of Instagram accounts that liked or commented a post
override static func primaryKey() -> String? {
return "id"
}
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
code <- map["code"]
imageUrl <- map["image_url"]
createdTime <- (map["created_time"], DateTransform())
weekday = Calendar.current.component(.weekday, from: createdTime)
likesCount <- map["likes_count"]
commentsCount <- map["comments_count"]
engagementCount <- map["engagement_count"]
}
}
================================================
FILE: insights-for-instagram/Networking/InstagramAPI.swift
================================================
import Foundation
import Moya
// MARK: - Provider setup
struct Constant {
static let baseURL = "https://insights-for-instagram.herokuapp.com/api/"
}
private func JSONResponseDataFormatter(_ data: Data) -> Data {
do {
let dataAsJSON = try JSONSerialization.jsonObject(with: data)
let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
return prettyData
} catch {
return data // fallback to original data if it can't be serialized.
}
}
let InstagramProvider = MoyaProvider<Instagram>(plugins: [NetworkLoggerPlugin(verbose: false, responseDataFormatter: JSONResponseDataFormatter)])
// MARK: - Provider support
private extension String {
var urlEscaped: String? {
return self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
}
}
public enum Instagram {
case userMedia(String)
}
extension Instagram: TargetType {
public var baseURL: URL { return URL(string: Constant.baseURL)! } // swiftlint:disable:this force_unwrapping
public var path: String {
switch self {
case .userMedia(let name):
guard let name = name.urlEscaped else { return "" }
return "users/\(name)/media"
}
}
public var method: Moya.Method {
return .get
}
public var parameterEncoding: ParameterEncoding {
return URLEncoding.default
}
public var task: Task {
return .requestPlain
}
public var validate: Bool {
switch self {
default:
return false
}
}
public var sampleData: Data {
return Data()
}
public var headers: [String: String]? {
return nil
}
}
public func url(_ route: TargetType) -> String {
return route.baseURL.appendingPathComponent(route.path).absoluteString
}
// MARK: - Response Handlers
extension Moya.Response {
func mapNSArray() throws -> [String: Any] {
let any = try self.mapJSON()
guard let array = any as? [String: Any] else {
throw MoyaError.jsonMapping(self)
}
return array
}
}
// MARK: - Provider support
func stubbedResponse(_ filename: String) -> Data? {
@objc class TestClass: NSObject { }
let bundle = Bundle(for: TestClass.self)
guard let path = bundle.path(forResource: filename, ofType: "json") else { return nil }
return (try? Data(contentsOf: URL(fileURLWithPath: path)))
}
================================================
FILE: insights-for-instagram/Services/DataService.swift
================================================
import RealmSwift
enum Weekday: Int {
case sunday = 1
case monday = 2
case tuesday = 3
case wednesday = 4
case thursday = 5
case friday = 6
case saturday = 7
}
class DataService {
static func media( for userName: String, completion: @escaping ( _ error: String?) -> Void) {
InstagramProvider.request(.userMedia(userName)) { result in
do {
let response = try result.dematerialize()
let value: [String: Any] = try response.mapNSArray()
guard let items = value["data"] as? [[String: Any]], items.isEmpty == false else {
completion(AppConfiguration.Messages.privateAccountMessage)
return
}
AppUserAccount().name = userName
importInstagramMedia(instagramMedia: items, completion: {
completion(nil)
})
} catch {
completion(error.localizedDescription)
}
}
}
// Creates/updates media
static func importInstagramMedia(instagramMedia: [[String: Any]], completion: @escaping () -> Void) {
DispatchQueue.global().async {
guard let realm = try? Realm() else { return }
realm.beginWrite()
for media in instagramMedia {
guard let instagramMedia = InstagramMedia(JSON: media) else {
continue
}
realm.add(instagramMedia, update: true)
}
try? realm.commitWrite()
DispatchQueue.main.async {
completion()
}
}
}
// Returns the top n most liked
static func mostLiked (with limit: Int ) -> [InstagramMedia] {
// paginating behavior isn’t necessary at all: https://realm.io/docs/swift/latest/#limiting-results
guard let realm = try? Realm() else { return [] }
let medias = realm.objects(InstagramMedia.self).sorted(byKeyPath: "commentsCount", ascending: false)
if medias.count > limit {
return Array(medias[0...limit])
}
return Array(medias)
}
// Returns last (n) weeks posted media
static func lastWeeksPosted (weeks: Int) -> [InstagramMedia] {
guard let realm = try? Realm() else { return [] }
guard let fromDate = Calendar.current.date(byAdding: .day, value: -(7 * weeks), to: Date()) else { return [] }
let predicate = NSPredicate(format: "createdTime > %@", fromDate as NSDate)
let medias = realm.objects(InstagramMedia.self).sorted(byKeyPath: "createdTime", ascending: false).filter(predicate)
return Array(medias)
}
// Returns best Engagement
static func bestEngagement (with limit: Int) -> [InstagramMedia] {
guard let realm = try? Realm() else { return [] }
let medias = realm.objects(InstagramMedia.self).sorted(byKeyPath: "engagementCount", ascending: false)
if medias.count > limit {
return Array(medias[0...limit])
}
return Array(medias)
}
// Returns the oldest media stored locally
static func instagramMediaIndex() -> (offset: String?, count: Int) {
guard let realm = try? Realm() else { return (nil, 0) }
let entries = realm.objects(InstagramMedia.self).sorted(byKeyPath: "createdTime", ascending: true)
if entries.isEmpty == false {
guard let offset = entries[0]["id"] as? String else { return (nil, 0) }
let count = entries.count
return (offset, count)
} else {
return (nil, 0)
}
}
// Weekday
// swiftlint:disable:next cyclomatic_complexity
static func weekday() -> String {
guard let realm = try? Realm() else { return "" }
var predicateArray = [NSPredicate]()
var groupbyArray = [[InstagramMedia]]()
var summedEngagementArray = [(Int, Int)]()
for weekday in 1...7 {
let predicate = NSPredicate(format: "weekday == %d", weekday)
predicateArray.append(predicate)
}
for weekday in predicateArray.indices {
let medias = Array(realm.objects(InstagramMedia.self).filter(predicateArray[weekday]))
groupbyArray.append(medias)
}
for weekday in groupbyArray.indices {
let array = groupbyArray[weekday]
summedEngagementArray.append(DataService.sumEngagement(for: array, day: weekday))
}
summedEngagementArray = summedEngagementArray.sorted(by: { $0.0 > $1.0 })
switch summedEngagementArray[0].1 {
case Weekday.sunday.rawValue:
return NSLocalizedString("Sunday.", comment: "")
case Weekday.monday.rawValue:
return NSLocalizedString("Monday.", comment: "")
case Weekday.tuesday.rawValue:
return NSLocalizedString("Tuesday.", comment: "")
case Weekday.wednesday.rawValue:
return NSLocalizedString("Wednesday.", comment: "")
case Weekday.thursday.rawValue:
return NSLocalizedString("Thursday.", comment: "")
case Weekday.friday.rawValue:
return NSLocalizedString("Friday.", comment: "")
case Weekday.saturday.rawValue:
return NSLocalizedString("Saturday.", comment: "")
default:
return NSLocalizedString("Weekday.", comment: "")
}
}
static func sumEngagement(for array: [InstagramMedia], day: Int) -> (Int, Int) {
var engagmentSum = 0
for media in array {
engagmentSum += media.engagementCount
}
return (engagmentSum, day + 1)
}
static func deleteAll() {
AppUserAccount().name = nil
guard let realm = try? Realm() else { return }
try? realm.write {
realm.deleteAll()
}
NotificationCenter.default.post(name: AppConfiguration.DefaultsNotifications.reload, object: nil)
}
}
================================================
FILE: insights-for-instagram.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
826DAD622CE2DE3A20EE8817 /* Pods_insights_for_instagramTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09F2374247510737687CC66A /* Pods_insights_for_instagramTests.framework */; };
970EC5CA084F08EC732835A5 /* Pods_insights_for_instagram.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 802F0FF4317368326E258C85 /* Pods_insights_for_instagram.framework */; };
AD4142E31F6FD78E00A83795 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AD4142E21F6FD78E00A83795 /* LaunchScreen.storyboard */; };
AD4142E41F6FD78E00A83795 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AD4142E21F6FD78E00A83795 /* LaunchScreen.storyboard */; };
AD61BABD1F6BDBF900307E92 /* AddAccountPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD61BABC1F6BDBF900307E92 /* AddAccountPresenter.swift */; };
AD6C69081F390A4E00039B66 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AD6C69061F390A4E00039B66 /* Main.storyboard */; };
AD6C690A1F390A4E00039B66 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AD6C69091F390A4E00039B66 /* Assets.xcassets */; };
AD7D8C371F73CCC60094697E /* InstagramAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD7D8C361F73CCC60094697E /* InstagramAPITests.swift */; };
ADB36EDB1F7306DA002844FD /* InstagramMediaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADB36EDA1F7306DA002844FD /* InstagramMediaTests.swift */; };
ADC34C1B20261413008E7875 /* InstagramMediaCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADC34C1A20261413008E7875 /* InstagramMediaCollectionViewCell.swift */; };
ADD71BCF1F6AE47800251445 /* InstagramAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BCE1F6AE47800251445 /* InstagramAPI.swift */; };
ADD71BD31F6AE48500251445 /* InstagramMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BD21F6AE48500251445 /* InstagramMedia.swift */; };
ADD71BDA1F6AE49B00251445 /* InsightsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BD61F6AE49B00251445 /* InsightsInteractor.swift */; };
ADD71BDD1F6AE49B00251445 /* InsightsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BD71F6AE49B00251445 /* InsightsPresenter.swift */; };
ADD71BE01F6AE49B00251445 /* InsightsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BD81F6AE49B00251445 /* InsightsViewController.swift */; };
ADD71BE31F6AE49B00251445 /* InstagramMediaSectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BD91F6AE49B00251445 /* InstagramMediaSectionTableViewCell.swift */; };
ADD71BEC1F6AE4AC00251445 /* AppUserAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BE61F6AE4AC00251445 /* AppUserAccount.swift */; };
ADD71BF21F6AE4AC00251445 /* DataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BE81F6AE4AC00251445 /* DataService.swift */; };
ADD71BF51F6AE4AC00251445 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BE91F6AE4AC00251445 /* AppConfiguration.swift */; };
ADD71BF81F6AE4AC00251445 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BEA1F6AE4AC00251445 /* AppDelegate.swift */; };
ADD71BFB1F6AE4AC00251445 /* AppExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BEB1F6AE4AC00251445 /* AppExtensions.swift */; };
ADD71C021F6AE4C100251445 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71BFE1F6AE4C100251445 /* AccountViewController.swift */; };
ADD71C081F6AE4C100251445 /* AddAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71C001F6AE4C100251445 /* AddAccountViewController.swift */; };
ADD71C0B1F6AE4C100251445 /* AddAccountInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADD71C011F6AE4C100251445 /* AddAccountInteractor.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
AD6C69141F390A4F00039B66 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = AD6C68F51F390A4E00039B66 /* Project object */;
proxyType = 1;
remoteGlobalIDString = AD6C68FC1F390A4E00039B66;
remoteInfo = "insights-for-instagram";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
09F2374247510737687CC66A /* Pods_insights_for_instagramTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_insights_for_instagramTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1879247EFBB7E37B2DF8A95E /* Pods-insights-for-instagramTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-insights-for-instagramTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-insights-for-instagramTests/Pods-insights-for-instagramTests.release.xcconfig"; sourceTree = "<group>"; };
25CF554A7A2BE8E283AEC682 /* Pods-insights-for-instagram.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-insights-for-instagram.debug.xcconfig"; path = "Pods/Target Support Files/Pods-insights-for-instagram/Pods-insights-for-instagram.debug.xcconfig"; sourceTree = "<group>"; };
7FADBF9E959DB8F02D5624CA /* Pods-insights-for-instagramTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-insights-for-instagramTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-insights-for-instagramTests/Pods-insights-for-instagramTests.debug.xcconfig"; sourceTree = "<group>"; };
802F0FF4317368326E258C85 /* Pods_insights_for_instagram.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_insights_for_instagram.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A7CD320FF647674A7285FBC0 /* Pods-insights-for-instagram.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-insights-for-instagram.release.xcconfig"; path = "Pods/Target Support Files/Pods-insights-for-instagram/Pods-insights-for-instagram.release.xcconfig"; sourceTree = "<group>"; };
AD4142E21F6FD78E00A83795 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
AD61BABC1F6BDBF900307E92 /* AddAccountPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountPresenter.swift; sourceTree = "<group>"; };
AD6C68FD1F390A4E00039B66 /* insights-for-instagram.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "insights-for-instagram.app"; sourceTree = BUILT_PRODUCTS_DIR; };
AD6C69071F390A4E00039B66 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
AD6C69091F390A4E00039B66 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
AD6C690E1F390A4E00039B66 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AD6C69131F390A4F00039B66 /* insights-for-instagramTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "insights-for-instagramTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
AD6C69191F390A4F00039B66 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AD7D8C361F73CCC60094697E /* InstagramAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstagramAPITests.swift; sourceTree = "<group>"; };
ADB36EDA1F7306DA002844FD /* InstagramMediaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstagramMediaTests.swift; sourceTree = "<group>"; };
ADC34C1A20261413008E7875 /* InstagramMediaCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstagramMediaCollectionViewCell.swift; sourceTree = "<group>"; };
ADD71BCE1F6AE47800251445 /* InstagramAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstagramAPI.swift; sourceTree = "<group>"; };
ADD71BD21F6AE48500251445 /* InstagramMedia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstagramMedia.swift; sourceTree = "<group>"; };
ADD71BD61F6AE49B00251445 /* InsightsInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightsInteractor.swift; sourceTree = "<group>"; };
ADD71BD71F6AE49B00251445 /* InsightsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightsPresenter.swift; sourceTree = "<group>"; };
ADD71BD81F6AE49B00251445 /* InsightsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsightsViewController.swift; sourceTree = "<group>"; };
ADD71BD91F6AE49B00251445 /* InstagramMediaSectionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstagramMediaSectionTableViewCell.swift; sourceTree = "<group>"; };
ADD71BE61F6AE4AC00251445 /* AppUserAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUserAccount.swift; sourceTree = "<group>"; };
ADD71BE81F6AE4AC00251445 /* DataService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataService.swift; sourceTree = "<group>"; };
ADD71BE91F6AE4AC00251445 /* AppConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = "<group>"; };
ADD71BEA1F6AE4AC00251445 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
ADD71BEB1F6AE4AC00251445 /* AppExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppExtensions.swift; sourceTree = "<group>"; };
ADD71BFE1F6AE4C100251445 /* AccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
ADD71C001F6AE4C100251445 /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = "<group>"; };
ADD71C011F6AE4C100251445 /* AddAccountInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountInteractor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
AD6C68FA1F390A4E00039B66 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
970EC5CA084F08EC732835A5 /* Pods_insights_for_instagram.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AD6C69101F390A4F00039B66 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
826DAD622CE2DE3A20EE8817 /* Pods_insights_for_instagramTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
AD6C68F41F390A4E00039B66 = {
isa = PBXGroup;
children = (
AD6C68FE1F390A4E00039B66 /* Products */,
AD6C68FF1F390A4E00039B66 /* insights-for-instagram */,
AD6C69161F390A4F00039B66 /* insights-for-instagramTests */,
FAD78C34808A08430765163F /* Pods */,
F3AD435598555D6DC2ACAD81 /* Frameworks */,
);
sourceTree = "<group>";
};
AD6C68FE1F390A4E00039B66 /* Products */ = {
isa = PBXGroup;
children = (
AD6C68FD1F390A4E00039B66 /* insights-for-instagram.app */,
AD6C69131F390A4F00039B66 /* insights-for-instagramTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
AD6C68FF1F390A4E00039B66 /* insights-for-instagram */ = {
isa = PBXGroup;
children = (
ADD71B8A1F6AE36500251445 /* Account */,
ADD71B891F6AE32A00251445 /* App */,
ADD71B8B1F6AE36F00251445 /* Insights */,
ADD71B8C1F6AE37C00251445 /* Models */,
ADD71B8D1F6AE38500251445 /* Networking */,
ADC34C192025FAD5008E7875 /* Services */,
AD6C69091F390A4E00039B66 /* Assets.xcassets */,
AD6C690E1F390A4E00039B66 /* Info.plist */,
AD4142E21F6FD78E00A83795 /* LaunchScreen.storyboard */,
AD6C69061F390A4E00039B66 /* Main.storyboard */,
);
path = "insights-for-instagram";
sourceTree = "<group>";
};
AD6C69161F390A4F00039B66 /* insights-for-instagramTests */ = {
isa = PBXGroup;
children = (
AD6C69191F390A4F00039B66 /* Info.plist */,
ADB36EDA1F7306DA002844FD /* InstagramMediaTests.swift */,
AD7D8C361F73CCC60094697E /* InstagramAPITests.swift */,
);
path = "insights-for-instagramTests";
sourceTree = "<group>";
};
ADC34C192025FAD5008E7875 /* Services */ = {
isa = PBXGroup;
children = (
ADD71BE81F6AE4AC00251445 /* DataService.swift */,
);
path = Services;
sourceTree = "<group>";
};
ADC34C1C202615FF008E7875 /* Views */ = {
isa = PBXGroup;
children = (
ADD71BD91F6AE49B00251445 /* InstagramMediaSectionTableViewCell.swift */,
ADC34C1A20261413008E7875 /* InstagramMediaCollectionViewCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
ADD71B891F6AE32A00251445 /* App */ = {
isa = PBXGroup;
children = (
ADD71BE91F6AE4AC00251445 /* AppConfiguration.swift */,
ADD71BEA1F6AE4AC00251445 /* AppDelegate.swift */,
ADD71BEB1F6AE4AC00251445 /* AppExtensions.swift */,
ADD71BE61F6AE4AC00251445 /* AppUserAccount.swift */,
);
path = App;
sourceTree = "<group>";
};
ADD71B8A1F6AE36500251445 /* Account */ = {
isa = PBXGroup;
children = (
ADD71BFE1F6AE4C100251445 /* AccountViewController.swift */,
ADD71C011F6AE4C100251445 /* AddAccountInteractor.swift */,
AD61BABC1F6BDBF900307E92 /* AddAccountPresenter.swift */,
ADD71C001F6AE4C100251445 /* AddAccountViewController.swift */,
);
path = Account;
sourceTree = "<group>";
};
ADD71B8B1F6AE36F00251445 /* Insights */ = {
isa = PBXGroup;
children = (
ADC34C1C202615FF008E7875 /* Views */,
ADD71BD61F6AE49B00251445 /* InsightsInteractor.swift */,
ADD71BD71F6AE49B00251445 /* InsightsPresenter.swift */,
ADD71BD81F6AE49B00251445 /* InsightsViewController.swift */,
);
path = Insights;
sourceTree = "<group>";
};
ADD71B8C1F6AE37C00251445 /* Models */ = {
isa = PBXGroup;
children = (
ADD71BD21F6AE48500251445 /* InstagramMedia.swift */,
);
path = Models;
sourceTree = "<group>";
};
ADD71B8D1F6AE38500251445 /* Networking */ = {
isa = PBXGroup;
children = (
ADD71BCE1F6AE47800251445 /* InstagramAPI.swift */,
);
path = Networking;
sourceTree = "<group>";
};
F3AD435598555D6DC2ACAD81 /* Frameworks */ = {
isa = PBXGroup;
children = (
802F0FF4317368326E258C85 /* Pods_insights_for_instagram.framework */,
09F2374247510737687CC66A /* Pods_insights_for_instagramTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
FAD78C34808A08430765163F /* Pods */ = {
isa = PBXGroup;
children = (
25CF554A7A2BE8E283AEC682 /* Pods-insights-for-instagram.debug.xcconfig */,
A7CD320FF647674A7285FBC0 /* Pods-insights-for-instagram.release.xcconfig */,
7FADBF9E959DB8F02D5624CA /* Pods-insights-for-instagramTests.debug.xcconfig */,
1879247EFBB7E37B2DF8A95E /* Pods-insights-for-instagramTests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
AD6C68FC1F390A4E00039B66 /* insights-for-instagram */ = {
isa = PBXNativeTarget;
buildConfigurationList = AD6C69271F390A4F00039B66 /* Build configuration list for PBXNativeTarget "insights-for-instagram" */;
buildPhases = (
A841561F091F6E1A316F0B6D /* [CP] Check Pods Manifest.lock */,
AD6C68F91F390A4E00039B66 /* Sources */,
AD6C68FA1F390A4E00039B66 /* Frameworks */,
AD6C68FB1F390A4E00039B66 /* Resources */,
B4A905D47AD41D1CAD9E0B84 /* [CP] Embed Pods Frameworks */,
E0E93F41DF7DC8CABFA0959C /* [CP] Copy Pods Resources */,
ADBDCEB92022681300183499 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = "insights-for-instagram";
productName = "insights-for-instagram";
productReference = AD6C68FD1F390A4E00039B66 /* insights-for-instagram.app */;
productType = "com.apple.product-type.application";
};
AD6C69121F390A4F00039B66 /* insights-for-instagramTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = AD6C692A1F390A4F00039B66 /* Build configuration list for PBXNativeTarget "insights-for-instagramTests" */;
buildPhases = (
5C69263025FD218D8650F06A /* [CP] Check Pods Manifest.lock */,
AD6C690F1F390A4F00039B66 /* Sources */,
AD6C69101F390A4F00039B66 /* Frameworks */,
AD6C69111F390A4F00039B66 /* Resources */,
480D1B7517B89E5E86BC99CC /* [CP] Embed Pods Frameworks */,
71875759ADCF7CD1A3B52512 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
AD6C69151F390A4F00039B66 /* PBXTargetDependency */,
);
name = "insights-for-instagramTests";
productName = "insights-for-instagramTests";
productReference = AD6C69131F390A4F00039B66 /* insights-for-instagramTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
AD6C68F51F390A4E00039B66 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Alex Di Mango";
TargetAttributes = {
AD6C68FC1F390A4E00039B66 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = 46BS3CW9YT;
LastSwiftMigration = 0900;
ProvisioningStyle = Automatic;
};
AD6C69121F390A4F00039B66 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = 46BS3CW9YT;
LastSwiftMigration = 0920;
ProvisioningStyle = Automatic;
TestTargetID = AD6C68FC1F390A4E00039B66;
};
};
};
buildConfigurationList = AD6C68F81F390A4E00039B66 /* Build configuration list for PBXProject "insights-for-instagram" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = AD6C68F41F390A4E00039B66;
productRefGroup = AD6C68FE1F390A4E00039B66 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
AD6C68FC1F390A4E00039B66 /* insights-for-instagram */,
AD6C69121F390A4F00039B66 /* insights-for-instagramTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
AD6C68FB1F390A4E00039B66 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AD4142E31F6FD78E00A83795 /* LaunchScreen.storyboard in Resources */,
AD6C690A1F390A4E00039B66 /* Assets.xcassets in Resources */,
AD6C69081F390A4E00039B66 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AD6C69111F390A4F00039B66 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AD4142E41F6FD78E00A83795 /* LaunchScreen.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
480D1B7517B89E5E86BC99CC /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagramTests/Pods-insights-for-instagramTests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework",
"${BUILT_PRODUCTS_DIR}/Quick/Quick.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagramTests/Pods-insights-for-instagramTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
5C69263025FD218D8650F06A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-insights-for-instagramTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
71875759ADCF7CD1A3B52512 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagramTests/Pods-insights-for-instagramTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
A841561F091F6E1A316F0B6D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-insights-for-instagram-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
ADBDCEB92022681300183499 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\"\n";
};
B4A905D47AD41D1CAD9E0B84 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagram/Pods-insights-for-instagram-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework",
"${BUILT_PRODUCTS_DIR}/Moya/Moya.framework",
"${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework",
"${BUILT_PRODUCTS_DIR}/Realm/Realm.framework",
"${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework",
"${BUILT_PRODUCTS_DIR}/Result/Result.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapper.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagram/Pods-insights-for-instagram-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
E0E93F41DF7DC8CABFA0959C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-insights-for-instagram/Pods-insights-for-instagram-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
AD6C68F91F390A4E00039B66 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ADD71BDD1F6AE49B00251445 /* InsightsPresenter.swift in Sources */,
ADD71BF21F6AE4AC00251445 /* DataService.swift in Sources */,
ADD71BE01F6AE49B00251445 /* InsightsViewController.swift in Sources */,
ADD71BEC1F6AE4AC00251445 /* AppUserAccount.swift in Sources */,
ADD71C021F6AE4C100251445 /* AccountViewController.swift in Sources */,
ADD71C0B1F6AE4C100251445 /* AddAccountInteractor.swift in Sources */,
ADD71BF51F6AE4AC00251445 /* AppConfiguration.swift in Sources */,
ADD71C081F6AE4C100251445 /* AddAccountViewController.swift in Sources */,
ADD71BF81F6AE4AC00251445 /* AppDelegate.swift in Sources */,
ADD71BCF1F6AE47800251445 /* InstagramAPI.swift in Sources */,
ADD71BE31F6AE49B00251445 /* InstagramMediaSectionTableViewCell.swift in Sources */,
AD61BABD1F6BDBF900307E92 /* AddAccountPresenter.swift in Sources */,
ADD71BDA1F6AE49B00251445 /* InsightsInteractor.swift in Sources */,
ADD71BD31F6AE48500251445 /* InstagramMedia.swift in Sources */,
ADC34C1B20261413008E7875 /* InstagramMediaCollectionViewCell.swift in Sources */,
ADD71BFB1F6AE4AC00251445 /* AppExtensions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AD6C690F1F390A4F00039B66 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
ADB36EDB1F7306DA002844FD /* InstagramMediaTests.swift in Sources */,
AD7D8C371F73CCC60094697E /* InstagramAPITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
AD6C69151F390A4F00039B66 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = AD6C68FC1F390A4E00039B66 /* insights-for-instagram */;
targetProxy = AD6C69141F390A4F00039B66 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
AD6C69061F390A4E00039B66 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
AD6C69071F390A4E00039B66 /* Base */,
);
name = Main.storyboard;
path = .;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
AD6C69251F390A4F00039B66 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
AD6C69261F390A4F00039B66 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
AD6C69281F390A4F00039B66 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 25CF554A7A2BE8E283AEC682 /* Pods-insights-for-instagram.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_TEAM = 46BS3CW9YT;
INFOPLIST_FILE = "insights-for-instagram/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "me.alexdimango.insights-for-instagram";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
AD6C69291F390A4F00039B66 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A7CD320FF647674A7285FBC0 /* Pods-insights-for-instagram.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 10;
DEVELOPMENT_TEAM = 46BS3CW9YT;
INFOPLIST_FILE = "insights-for-instagram/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "me.alexdimango.insights-for-instagram";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
AD6C692B1F390A4F00039B66 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7FADBF9E959DB8F02D5624CA /* Pods-insights-for-instagramTests.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = 46BS3CW9YT;
INFOPLIST_FILE = "insights-for-instagramTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "me.alexdimango.insights-for-instagramTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/insights-for-instagram.app/insights-for-instagram";
};
name = Debug;
};
AD6C692C1F390A4F00039B66 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1879247EFBB7E37B2DF8A95E /* Pods-insights-for-instagramTests.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = 46BS3CW9YT;
INFOPLIST_FILE = "insights-for-instagramTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "me.alexdimango.insights-for-instagramTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/insights-for-instagram.app/insights-for-instagram";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
AD6C68F81F390A4E00039B66 /* Build configuration list for PBXProject "insights-for-instagram" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AD6C69251F390A4F00039B66 /* Debug */,
AD6C69261F390A4F00039B66 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AD6C69271F390A4F00039B66 /* Build configuration list for PBXNativeTarget "insights-for-instagram" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AD6C69281F390A4F00039B66 /* Debug */,
AD6C69291F390A4F00039B66 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AD6C692A1F390A4F00039B66 /* Build configuration list for PBXNativeTarget "insights-for-instagramTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AD6C692B1F390A4F00039B66 /* Debug */,
AD6C692C1F390A4F00039B66 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = AD6C68F51F390A4E00039B66 /* Project object */;
}
================================================
FILE: insights-for-instagram.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:insights-for-instagram.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: insights-for-instagram.xcodeproj/xcshareddata/xcschemes/insights-for-instagram.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C68FC1F390A4E00039B66"
BuildableName = "insights-for-instagram.app"
BlueprintName = "insights-for-instagram"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C69121F390A4F00039B66"
BuildableName = "insights-for-instagramTests.xctest"
BlueprintName = "insights-for-instagramTests"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C691D1F390A4F00039B66"
BuildableName = "insights-for-instagramUITests.xctest"
BlueprintName = "insights-for-instagramUITests"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C68FC1F390A4E00039B66"
BuildableName = "insights-for-instagram.app"
BlueprintName = "insights-for-instagram"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C68FC1F390A4E00039B66"
BuildableName = "insights-for-instagram.app"
BlueprintName = "insights-for-instagram"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AD6C68FC1F390A4E00039B66"
BuildableName = "insights-for-instagram.app"
BlueprintName = "insights-for-instagram"
ReferencedContainer = "container:insights-for-instagram.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: insights-for-instagram.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:insights-for-instagram.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: insights-for-instagramTests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleVersion</key>
<string>10</string>
</dict>
</plist>
================================================
FILE: insights-for-instagramTests/InstagramAPITests.swift
================================================
import Quick
import Nimble
import Alamofire
@testable
import insights_for_instagram
import Moya
class InstagramAPITests: QuickSpec {
override func spec() {
var provider: MoyaProvider<Instagram>!
beforeEach {
provider = MoyaProvider<Instagram>(stubClosure: MoyaProvider.immediatelyStub)
}
it("returns stubbed data for user media request") {
var json: String?
let target: Instagram = .userMedia("")
provider.request(target) { result in
if case let .success(response) = result {
json = String(data: response.data, encoding: .utf8)
}
}
let sampleData = String(data: target.sampleData, encoding: .utf8)
expect(json).to(equal(sampleData))
}
}
}
================================================
FILE: insights-for-instagramTests/InstagramMediaTests.swift
================================================
import Quick
import Nimble
import Result
import Alamofire
@testable import insights_for_instagram
import ObjectMapper
class InstagramMediaTests: QuickSpec {
override func spec() {
it("create from JSON") {
let id = "id-xyz"
let code = "xyx"
let imageUrl = ""
let createdTime = Date()
let likesCount = 4
let commentsCount = 3
let engagementCount = 7
let json = ["id": id, "code": code, "image_url": imageUrl, "created_time": createdTime, "likes_count": likesCount, "comments_count": commentsCount, "engagement_count": engagementCount] as [String: Any]
let media = InstagramMedia(JSON: json)
expect(media?.id) == id
expect(media?.code) == code
expect(media?.likesCount) == 4
expect(media?.commentsCount) == 3
expect(media?.engagementCount) == 4 + 3
}
}}
gitextract_zbz5km2e/
├── .gitignore
├── .swiftlint.yml
├── .travis.yml
├── LICENSE
├── Podfile
├── README.md
├── fastlane/
│ ├── Appfile
│ ├── Fastfile
│ └── README.md
├── insights-for-instagram/
│ ├── Account/
│ │ ├── AccountViewController.swift
│ │ ├── AddAccountInteractor.swift
│ │ ├── AddAccountPresenter.swift
│ │ └── AddAccountViewController.swift
│ ├── App/
│ │ ├── AppConfiguration.swift
│ │ ├── AppDelegate.swift
│ │ ├── AppExtensions.swift
│ │ └── AppUserAccount.swift
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── chevron_location_.imageset/
│ │ │ └── Contents.json
│ │ ├── icons8-Male User Filled_30.imageset/
│ │ │ └── Contents.json
│ │ ├── line-chart-graph.imageset/
│ │ │ └── Contents.json
│ │ ├── placeHolder.imageset/
│ │ │ └── Contents.json
│ │ └── user-icon.imageset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── Insights/
│ │ ├── InsightsInteractor.swift
│ │ ├── InsightsPresenter.swift
│ │ ├── InsightsViewController.swift
│ │ └── Views/
│ │ ├── InstagramMediaCollectionViewCell.swift
│ │ └── InstagramMediaSectionTableViewCell.swift
│ ├── LaunchScreen.storyboard
│ ├── Models/
│ │ └── InstagramMedia.swift
│ ├── Networking/
│ │ └── InstagramAPI.swift
│ └── Services/
│ └── DataService.swift
├── insights-for-instagram.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ └── contents.xcworkspacedata
│ └── xcshareddata/
│ └── xcschemes/
│ └── insights-for-instagram.xcscheme
├── insights-for-instagram.xcworkspace/
│ └── contents.xcworkspacedata
└── insights-for-instagramTests/
├── Info.plist
├── InstagramAPITests.swift
└── InstagramMediaTests.swift
Condensed preview — 42 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (153K chars).
[
{
"path": ".gitignore",
"chars": 1499,
"preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
},
{
"path": ".swiftlint.yml",
"chars": 198,
"preview": "disabled_rules:\n - trailing_whitespace\n - line_length\n - type_name\n - identifier_name\n\nopt_in_rules:\n - empty_count"
},
{
"path": ".travis.yml",
"chars": 478,
"preview": "language: objective-c\nos: osx\nosx_image: xcode9.2\nxcode_workspace: insights-for-instagram.xcworkspace\npodfile: Podfile\nc"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "MIT License\n\nCopyright (c) 2017 alexdimango.me, Inc.\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "Podfile",
"chars": 518,
"preview": "# Uncomment the next line to define a global platform for your project\n# platform :ios, '9.0'\n\ntarget 'insights-for-inst"
},
{
"path": "README.md",
"chars": 2699,
"preview": "# Insights for Instagram\nA simple iOS Instagram's media insights App.\n\n. The extraction includes 42 files (137.4 KB), approximately 37.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.