Skip to main content

Getting Timeline

The CloudikePhotos SDK provides features for building a timeline from the device's gallery and server, as well as for automatic photo download from a device.

Initialization

Assume that an instance of CloudikePhotos - the Photos Manager has been created.

let photosManager: CloudikePhotos

There are two ways to start CloudikePhotos: with the photo auto uploading enabled and without it.

public final class CloudikePhotos {
// ...
public func configureWithUploadSettings(_ uploadSettings: CloudikeAutoUploadSettings, in background: Bool = false, with completion: ((NSError?) -> Void)?)
// ...
}

// ...

photosManager.configureWithUploadSettings(...)

Flag background tells that app running in background mode. If the isAutoUploadEnabled flag in CloudikeAutoUploadSettings is false, to scan the local gallery, after calling the method for configuring the SDK, you need to call the method:

public final class CloudikePhotos {
// ...
public func updateLocal()
// ...
}

// ...

photosManager.updaLocal()

Error handling

Most of CloudikePhotosKit APIs returns CloudikePhotosError object in callback:

public enum CloudikePhotosError: Error {

case unknown
case cancelled
case noInternet
case unauthorized
case lowStorageSpace
case lowDeviceStorageSpace
case forbidden
case invalidParameters
case preconditionFailed

// ...

}

CloudikePhotosError have following cases:

  • unknown: unknown error
  • cancelled: operation was cancelled by requesting api
  • noInternet: no connection to internet
  • unauthorized: authorization cretentials invalid
  • lowStorageSpace: low cloud disk space
  • lowDeviceStorageSpace: low device disk space
  • forbidden: internal server error
  • invalidParameters: invalid request
  • preconditionFailed: invalid request conditions

Read Photos from the Cloud Storage and Timeline Reloading

To initially get and update the gallery stored on the server, you need to call the method:

public protocol TimelineUpdateService {
func update(_ completion: ((Bool, CloudikePhotosError?) -> ())?)
}

// ...

photosManager.timelineService.update() {
// completion block
}

Timeline Building a list of Timeline Photos

CloudikePhotosKit provides photo information for the client application through the special TimelineAdapter object, which is based on NSFetchedResultsController.

To initialize CloudikeAlbumsTimelineAdapter, you need to call the following methods:


override func viewDidLoad() {
// ...

let adapter = photosManager.timelineService.timelineAdapter(collectionView)

// ...
}

collectionView is the instance of the UICollectionView object used to display photos in the Timeline.

UICollectionView

To build the timeline, an adapter is provided. It implements the NSFetchedResultsController methods. Building a timeline can be implemented using UICollectionView for which the application needs to implement the uicollectionviewdelegate delegate methods and UICollectionViewDataSource

iOS13 or Higher Support

In iOS13 or later, you should implement closures to initialize objects to display section headers and cells. For iOS13 or higher, the Timeline Adapter uses UICollectionViewDiffableDataSource which is HIGHLY RECOMMENDED for iOS13 or higher.

Timeline Cell Configuration for iOS13 or Higher - UICollectionViewCell

Refer to the following documents.

var cell: ((UICollectionView, IndexPath)->UICollectionViewCell)?

Your code (probably in ViewDidLoad) might look like this:

override func viewDidLoad() {
// ...

adapter.cell = { [weak self] collectionView, indexPath in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: <YourCustomCellReuseIdentifier>, for: indexPath)
// setup your header here...
self?.yourCellConfigurationMethod(cell)
return cell
}

// ...
}

Timeline Section Header Configuration for iOS13 or Higher - UICollectionReusableView

Uicollectionreusableview is used for section headers. The headers are configured using closures:

var supplementaryView: ((_ collectionView: UICollectionView, _ kind: String, _ indexPath: IndexPath, _ data: CloudikeTimelineHeaderData?) -> UICollectionReusableView)?

Your code (probably in ViewDidLoad) might look lile this:

override func viewDidLoad() {
// ...

adapter.supplementaryView = { [weak self] collectionView, kind, indexPath, _ in
var header: UICollectionReusableView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, for: indexPath)
// setup your header here...


let timelineSectionHeaderTitle = self.adapter.nameForSection(indexPath.section)

if let view = view as? YourCustomHeaderReusableView {
view.title = timelineSectionHeaderTitle
}

return header
}

// ...
}

Adapter Preparings for iOS13 or Higher

This method should be called after setting up the clousures:


override func viewDidLoad() {
// ...

if #available(iOS 13.0, *) {
self.adapter.configure()
}

// ...
}

iOS12 or Lower Support

To display header and cell on devices with iOS12 or lower, you should implement the following delegate methods.

Timeline Cell Configuration for iOS12 or Lower - UICollectionViewCell

To display cells on iOS 12 or lower, use the delegate method, which is not called in the IOS 13 runtime if the UICollectionViewDiffableDataSource methods are implemented:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

Your code might look like this:


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimelineCollectionViewAdapter.timelineCellReuseIdentifier, for: indexPath)
// setup your header here...
self?.yourCellConfigurationMethod(cell)
return cell
}

Timeline Section Header Configuration for iOS12 or Lower - UICollectionReusableView

To display section headers on iOS 12 or lower, the delegate method is used, which is not called in the iOS13 runtime if the UICollectionViewDiffableDataSource methods are implemented:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView

Your code might look like this:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { [weak self] in
guard kind == UICollectionView.elementKindSectionHeader else {
return UICollectionReusableView()
}
var view: TimelineHeaderReusableView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, for: indexPath)
// setup your header here...

let timelineSectionHeaderTitle = self.adapter.nameForSection(indexPath.section)

if let view = view as? YourCustomHeaderReusableView {
view.title = timelineSectionHeaderTitle
}

return view
}

Timeline Cell Objects

The Timeline cell data is contained in the CloudikeMediaItem objects. If you want to track the status of uploading of an object to the server, then when you receive the CloudikeMediaItem object, you must configure a closure that returns the CloudikeUploadStatus object.

To get the data to configure the cell of the timeline, you need to call the method:

public protocol TimelineAdapter: BaseAdapter {
func mediaItem(_ indexPath: IndexPath, with didUpdateBlock: ((CloudikeUploadStatus?) -> Void)?) -> CloudikeMediaItem?
}

So, your code might look like this:


func yourCellConfigurationMethod(cell: UICollectionViewCell) {
if let timelineCell = cell as? YourCustomTimelineCollectionViewCell {
if let mediaItem = self.adapter.mediaItem(indexPath, with: { [weak timelineCell] uploadStatus in
DispatchQueue.main.async {
// Here you need to update the auto upload status for the cell.
timelineCell?.yourUpdateUploadStatusMethod(uploadStatus)
}
}) {
// Here we configure your custom cell.

}
}
}

UICollectionViewDataSource

For implementing methods, the Delegate CloudikePhotosKit methods are used. You need to implement the UICollectionViewDataSource methods for your UICollectionView.

// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.adapter.itemsCount(section)
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.adapter.sectionsCount()
}

Reaction for Changes in Content

To perform additional actions when updating CloudikePhotosKit data, you can configure the closure:

    adapter.didChangeContent = {
...your code here
}

Timeline Reload

It is recommended to update the timeline in this way:

    public func update(fullRefresh: Bool, with completion: (() -> Void)?) {
photosManager.timelineService.update(fullRefresh: fullRefresh) { [weak self] _ in
self?.adapter.fetch{
completion?()
}
}
}

PHAuthorizationStatus

You should request PHAuthorizationStatus before starting inintialization process for CloudikePhotosKit or you can allow framework to request PHAuthorizationStatus by setting the CloudikePhotosSettings's isPermissionsRequestAllowed parameter to TRUE

Plist file: Privacy - Photo Library Usage Description

You should add the value Privacy - Photo Library Usage Description to your app's plist file.

Logout

To shut down Cloudike Photos, stop auto upload and clear records in the database, for example, to change the user, you need to call the "logout" method.

photosManager.logout {
// ... completion block
}