๐ŸŒ An Unsplash app for iOS

Overview

๐ŸŒ Papr

Build Status Build Status

Papr is an unofficial Unsplash app for iOS.

๐Ÿƒโ€โ™‚๏ธ Getting Started

git clone https://github.com/jdisho/Papr.git
cd Papr
pod install
open Papr.xcworkspace # or xed .

โš™๏ธ Setup

To be able to log in during development, you'll need a Client ID and Client Secret.

To get these, register a new OAuth application on Unsplash.

Make sure the Authorization callback URL is set to papr://unsplash. The others can be filled in as you wish.

To add the Client ID and Client Secret to the App, follow these steps:

  1. In Xcode, go to Product> Scheme > Manage Schemes...
  2. Select Papr and click Edit...
  3. Go to Run > Arguments
  4. Add your Client ID (UNSPLASH_CLIENT_ID as key) and Client Secret (UNSPLASH_CLIENT_SECRET) to the Environment Variables.

๐ŸŽ‰ Why am I building this?

  1. Pushing RxSwift to its limits. ๐Ÿ”ฅ
  2. MVVM + Coordinator
  3. Using Codable, RxDataSources, Action.
  4. Exploring Unsplash and its API
  5. Fun thing!

โค๏ธ Contributing

I intend for this project to be more as an educational resource, learn by open sourcing.

I am very open for feedback and contribution.

Comments
  • Feature/present interactive

    Feature/present interactive

    https://github.com/jdisho/Papr/issues/55

    This pull makes possible interactively (with gesture) to close detail photo

    I did some changes on Coordinator pop function. I made it cancelable (when dispose the observable do not set current controller)

    opened by vaderdan 5
  • SceneCoordinator Transition

    SceneCoordinator Transition

    First of all I would like to thank you for this amazing project. Truly your project has become an educational resource for me!

    My question is about the SceneCoordinator. I implemented the Coordinator with the same logic without RX, because I don't have the enough confidence to start using RX in projects for production. I have only one missing link to intercept when the View Controllers pop or a tab bar item is changed you make the SceneCoordinator the navigationControllerDelegate and tabBarControllerDelegate. I am wondering what the difference between this:

    currentViewController.navigationController?.rx.delegate.setForwardToDelegate(self, retainDelegate: false)
    currentViewController.tabBarController?.rx.delegate.setForwardToDelegate(self, retainDelegate: false)
    

    And this code is:

    currentViewController.navigationController?.delegate = self
    currentViewController.tabBarController?.delegate = self
    

    I'm guessing RX has a fancy way of preventing memory leaks in this case. How can I make my own implementation of setForwardToDelegate? Again thank you very much!

    question 
    opened by serjooo 5
  • Add error handling to avoid crash if getting error reponse from loading image

    Add error handling to avoid crash if getting error reponse from loading image

    If any of the image requests get an error response, it will crash since binding error. Catch error and return an empty observable or a placeholder image to avoid the crash.

    opened by xingye 2
  • XCode 11.2.1 Compile failed

    XCode 11.2.1 Compile failed

    git clone https://github.com/jdisho/Papr.git cd Papr pod install open Papr.xcworkspace

    and cmd+R compile failed ? Has anyone encountered this problem ?

    WeChatd95ec537b27483f5d74721904b51ee12

    opened by HotWordland 2
  • SceneCoodinator.swift=>actualViewController=>return issue

    SceneCoodinator.swift=>actualViewController=>return issue

    Papr/Coodinator/SceneCoodinator.swift

    static func actualViewController(for viewController: UIViewController) -> UIViewController {
        var controller = viewController
        if let tabBarController = controller as? UITabBarController {
            guard let selectedViewController = tabBarController.selectedViewController else {
                return tabBarController
            }
            controller = selectedViewController
        }
        if let navigationController = viewController as? UINavigationController {
            return navigationController.viewControllers.first!
        }
        -return viewController  
        +return controller
    }
    
    opened by spiderbob 2
  • fix PinterestLaout (DelegateProxy protocol was crashing due missing pโ€ฆ

    fix PinterestLaout (DelegateProxy protocol was crashing due missing pโ€ฆ

    https://github.com/jdisho/Papr/issues/43

    @jdisho fixes RxDataSources crashes with a custom UICollectionViewLayout

    When image cell sets image we update the PinterestLayout sizes with that size and invalidate the layout

    opened by vaderdan 2
  • Change location of Unsplash Constants

    Change location of Unsplash Constants

    If app is runned from Xcode, it is all ok, otherwise app will do nothing because can't obtain Unsplash Constants from Enviroment Variables. See https://stackoverflow.com/a/14214923 .

    opened by ghost 2
  • Convert all SVG images to PDF

    Convert all SVG images to PDF

    Closes issue #96

    This PR converts all SVG images added for iOS 13 to support SFSymbols to PDF. This way we can still use them as vector images. For each medium/small size and medium/regular weight an image was set in the assets accordingly.

    Known issue while working on PR:

    App crashes still on iOS 12.2 in the HomeViewController on line 82:

    outputs.isRefreshing
    .execute { [weak self] isRefreshing in
        if isRefreshing {
            self?.collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .top, animated: false)
            self?.collectionView.setContentOffset(CGPoint(x: 0.0, y: -(self?.refreshControl.frame.height ?? 0.0)), animated: true)
        } else {
            self?.collectionView.setContentOffset(.zero, animated: true)
        }
    }
    .bind(to: refreshControl.rx.isRefreshing)
    .disposed(by: disposeBag)
    

    Commenting out the isRefreshing code solves the problem as the app crashes on scrolling logic.

    opened by serjooo 1
  • Papr crashes in iOS 12 and lower.

    Papr crashes in iOS 12 and lower.

    Since SVG format is not supported in iOS, the app crashes when it tries to access an image with a given resource name.

    static var arrowUpRight: UIImage {
        if #available(iOS 13.0, *) {
            return UIImage(systemName: "arrow.up.right", withConfiguration: symbolConfigurationMedium)!#
        }
        return UIImage(imageLiteralResourceName: "arrow.up.right") // #CRASH
    }
    

    We can solve this issue by (I would like to avoid having a dependency for this use case) by converting the SVGs into .png or supporting iOS13 and higher.

    Thoughts on this @serjooo (since you closed #89) ?

    (This app is not planned to be released, so supporting only the latest iOS version seems fine to me.)

    bug 
    opened by jdisho 1
  • Present photos interactive

    Present photos interactive

    We could add pan gesture recogniser on DetailsScreen and dismiss the photo interactively

    https://github.com/HeroTransitions/Hero/wiki/Interactive-Transition

    @jdisho wdyt

    feature 
    opened by vaderdan 1
  • Improve UX of photo list

    Improve UX of photo list

    I will be cool if we could implement parallax images scroll

    https://github.com/jdisho/ParallaxScrolling

    (images in each cell moves with different speed than the scroll)

    enhancement 
    opened by vaderdan 1
  • Unable to get the latest 'isLikedByUser'.

    Unable to get the latest 'isLikedByUser'.

    I imitated your code. After I press the select button, isLikedByUser is always the initial value.

    PostViewCellModel.swift

    import Foundation
    import RxSwift
    import Action
    import RealmSwift
    
    protocol PostViewCellModelInput {
        var selectPostAction: Action<Post, Post> { get }
        var unselectPostAction: Action<Post, Post> { get }
    }
    protocol PostViewCellModelOutput {
        var postData: Observable<Post> { get }
        var isSelected: Observable<Bool> { get }
    }
    protocol PostViewCellModelType {
        var inputs: PostViewCellModelInput { get }
        var outputs: PostViewCellModelOutput { get }
    }
    
    final class PostViewCellModel: PostViewCellModelType, PostViewCellModelInput, PostViewCellModelOutput {
        var inputs: PostViewCellModelInput { return self }
        var outputs: PostViewCellModelOutput { return self }
        
        lazy var selectPostAction: Action<Post, Post> = {
            Action<Post, Post> { [unowned self] post in
                let configuration = Realm.Configuration(deleteRealmIfMigrationNeeded: true)
                let realm = try! Realm(configuration: configuration)
                
                let sharePost = SharePost()
                sharePost.ref = post.ref!
                sharePost.title = post.title
                sharePost.summary = post.summary
                sharePost.url = post.url
    
                try! realm.write {
                    realm.add(sharePost, update: .modified)
                }
    
                return .just(post)
            }
        }()
        
        lazy var unselectPostAction: Action<Post, Post> = {
            Action<Post, Post> { [unowned self] post in
                let configuration = Realm.Configuration(deleteRealmIfMigrationNeeded: true)
                let realm = try! Realm(configuration: configuration)
                let sharePost = realm.objects(SharePost.self).filter("ref = %@", post.ref!)
                
                if (sharePost.count > 0) {
                    try! realm.write {
                        realm.delete(sharePost.first!)
                    }
                }
                
                return .just(post)
            }
        }()
     
        var postData: Observable<Post>
        var isSelected: Observable<Bool>
        
        private let service: PostServiceType
        private let cache: Cache
        private let sceneCoordinator: SceneCoordinatorType
        
        init(post: Post, service: PostServiceType = PostService(), cache: Cache = Cache.shared, sceneCoordinator: SceneCoordinatorType = SceneCoordinator.shared) {
            self.service = service
            self.cache = cache
            self.sceneCoordinator = sceneCoordinator
            
            postData = Observable.just(post)
            let cachedPostData = cache.getObject(ofType: Post.self, withId: post.ref ?? "").unwrap()
            
            isSelected = self.postData.merge(with: cachedPostData)
                .map { post in
                    let configuration = Realm.Configuration(deleteRealmIfMigrationNeeded: true)
                    let realm = try! Realm(configuration: configuration)
    
                    if realm.object(ofType: SharePost.self, forPrimaryKey: post.ref!) != nil {
                        return true
                    } else {
                        return false
                    }
                }.unwrap()
        }
    }
    

    PostViewCell.swift

    import Foundation
    import UIKit
    import RxSwift
    import RxCocoa
    import RealmSwift
    
    class PostViewCell: UITableViewCell, BindableType, NibIdentifiable & ClassIdentifiable {
        var viewModel: PostViewCellModelType!
        
        @IBOutlet var selectButton: UIButton!
        
        private var disposeBag = DisposeBag()
    
        func bindViewModel() {
            let inputs = viewModel.inputs
            let outputs = viewModel.outputs
            
            Observable.combineLatest(outputs.isSelected, outputs.postData)
                .bind { [weak self] in
                    self?.selectButton.rx.bind(to: $0 ? inputs.unselectPostAction: inputs.selectPostAction, input: $1)
                }
                .disposed(by: disposeBag)
    
            outputs.isSelected
                .map { $0 ? UIColor.init(red: 210 / 255, green: 233 / 255, blue: 1, alpha: 1) : .clear }
                .bind(to: self.rx.backgroundColor)
                .disposed(by: disposeBag)
        }
    }
    
    opened by RoyDeng 0
  • HomeViewController crashes in iOS12 and lower when trying to refresh

    HomeViewController crashes in iOS12 and lower when trying to refresh

    As @serjooo mentioned:

    App crashes still on iOS 12 in the HomeViewController on line 82:

    outputs.isRefreshing
    .execute { [weak self] isRefreshing in
        if isRefreshing {
            self?.collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at: .top, animated: false)
            self?.collectionView.setContentOffset(CGPoint(x: 0.0, y: -(self?.refreshControl.frame.height ?? 0.0)), animated: true)
        } else {
            self?.collectionView.setContentOffset(.zero, animated: true)
        }
    }
    .bind(to: refreshControl.rx.isRefreshing)
    .disposed(by: disposeBag)
    

    Commenting out the isRefreshing code solves the problem as the app crashes on scrolling logic.

    bug 
    opened by jdisho 0
  • Swipe left-right to see prev-next photo

    Swipe left-right to see prev-next photo

    On the photo detail, swipe left-right to see the prev-next photo. This means that the PhotoDetail.swift should be a UICollectionViewCell instead of a UIViewController.

    feature 
    opened by jdisho 0
Owner
Joan Disho
iOS Engineer
Joan Disho
A random photo generator from unsplash for iOS

Random Photo App for IOS How it works It is a random photo generator from unsplash. Everytime you press the button, a random photo appears and the bac

Stefan Istoc 2 Feb 2, 2022
Jogendra 113 Nov 28, 2022
Source code for the iOS app Screenshotter, on the App Store

Screenshotter This is the source code for the iOS app Screenshotter, available on the App Store. General Project Info Screenshotter is an Objective C

Riz 94 Mar 25, 2022
SwiftUI App Icon Generator App for iOS & macOS Catalyst

SwiftUI App Icon Generator App for iOS & macOS Catalyst Generate Asset Icons easily to your iPhone, iPad, Mac, and Apple Watch Features The app has se

Alfian Losari 21 Sep 14, 2022
Microblog-ref-app - A Twitter like social media app that users can share their moments

HiPlace - iOS Table of Contents Introduction HMS Services Getting Started Suppor

null 2 Jan 3, 2022
Photo-Sharing-App - Photo Sharing App With Swift

Photo Sharing App You can register and log in to this application and share your

YaฤŸฤฑz Savran 2 Jun 14, 2022
Lightbox is a convenient and easy to use image viewer for your iOS app

Lightbox is a convenient and easy to use image viewer for your iOS app, packed with all the features you expect: Paginated image slideshow. V

HyperRedink 1.5k Dec 22, 2022
In-app screen recording using ReplayKit in iOS. Written in Swift 5 on Xcode 12.3

In-App-ScreenRecording-iOS In-app screen recording using ReplayKit in iOS. Written in Swift 5 on Xcode 12.3 Features: Recording application screen onl

Ahmed Abdelkarim 4 Dec 23, 2022
Lightweight iOS Photo Blur App

Blurry Blurry is the go-to image blurring tool to help you apply beautiful blurs for your photos. It is perfect for creating wallpapers, backgrounds,

Andy 17 Nov 22, 2022
๐Ÿ“ฑiOS app to extract full-resolution video frames as images.

Frame Grabber is a focused, easy-to-use iOS app to extract full-resolution video frames as images. Perfect to capture and share your favorite video mo

Arthur Hammer 319 Jan 7, 2023
An iOS app that helps you check, edit and delete metadata of photos, including but not limited to EXIF, TIFF...

MetaX A simple iOS app that helps you check, edit and delete metadata of photos, including but not limited to EXIF, TIFF... Feature List main metadata

Yuhan Chen 189 Dec 29, 2022
Inspired by HBO's Silicon Valley: SeeFood is an iOS app that uses CoreML to detect various dishes

SeeFood For a step by step guide on how to build SeeFood: How to train your own model for CoreML. . Video Demo Follw me on Twitter Prerequisites: Xcod

Reza Shirazian 448 Nov 17, 2022
Source code for iOS app "Photos for VK" โ€” albums and photos manager for social network VKontakte

VK Photos (formally Photos for VK) VK Photos is an iOS app for manage albums and photos in social network VKontakte (vk.com) Screenshots Disclaimer โš ๏ธ

Yury S. 29 Oct 8, 2022
iOS app to automagically control device torch/flash and capture photos

BlobStar โœจ Version franรงaise ???? iOS application to automagically control the device torch/flash and capture photos. The software was quickly drafted

Ninja Inc 1 Oct 11, 2021
An image cropper / photo cropper for iOS like in the Contacts app with support for landscape orientation.

RSKImageCropper An image cropper for iOS like in the Contacts app with support for landscape orientation. Installation RSKImageCropper requires iOS 9.

Ruslan Skorb 2.4k Jan 5, 2023
A demo of face recognition SwiftUI app on iOS. Based on Vision, OpenCV, Dlib and ResNet.

iOS-FaceRecognizer A demo of face recognition SwiftUI app on iOS, build for iPad. Based on Vision, OpenCV, Dlib and ResNet. Features Add face image an

js_john 11 Aug 20, 2022
An app that uses Multipeer Connectivity to play a video across 6 different iOS screens with an additional phone acting as a Main/Control phone.

MultiScreenApp An app that uses Multipeer Connectivity to play a video across 6 different iOS screens with an additional phone acting as a Main/Contro

Vedant 113 Nov 21, 2022
ImagePicker : an all-in-one camera solution for your iOS app

Description ImagePicker is an all-in-one camera solution for your iOS app. It lets your users select images from the library and take pictures at the

ร–zgรผr OdabaลŸฤฑ 1 Dec 1, 2021
Starter project for Mars rover photos iOS app.

MarzyPan is an app that allows users to view photos taken by Mars rovers during their time on the planet. This is a starter project that has some UI

Ruben Hansen-Rojas 0 Dec 30, 2021