Creating an Advanced Local Capacitor Plugin
In the introduction to Capacitor module, we already looked at the basic outline of how to build a custom native plugin to access native functionality. If there isn’t already an official Capacitor API or a community plugin to access what you want, sometimes you might need to build the native integration yourself.
However, the plugin we created was very simple. All we did was call a method to display a native alert. In this lesson, we are going to take a look at something a little more advanced that involves two way communication - we send some kind of data across the native bridge, and we receive some kind of data back.
Get the Nth Photo
What we are going to try to do in this lesson is to get the “n”th latest photo from the users gallery/photo library. If we supply 0
it should give us the latest photo, if we supply 1
it should give us the second to last photo the user has taken, and if we supply 20
it should give us the 21st last photo taken.
We are going to follow the same basic process as in the basic module, but we will be utilising some more advanced code.
NOTE: Remember to run ionic cap sync
or npx cap sync
before opening the native projects.
NOTE: We will only be implementing this plugin for iOS. Retrieving photos is more complex from Android as there is not a consistent place where photos are stored, so programatically retrieving the “5th latest photo” is difficult.
Adding Native Code for iOS
First, you will need to open Xcode:
npx cap open ios
or
ionic cap open ios
Within XCode, you will need to expand App on the left, and then select the App target within that:
You can then right click it to create a new file - you should choose a Swift file and name it something like NthPhotoPlugin.swift
. You could then add the following code to the file:
import Capacitor
import Photos
@objc(NthPhotoPlugin)
public class NthPhotoPlugin: CAPPlugin {
@objc func getNthPhoto(_ call: CAPPluginCall) {
let photoAt = Int(call.getString("photoAt") ?? "0") ?? 0
let photos = PHPhotoLibrary.authorizationStatus()
if photos == .notDetermined {
PHPhotoLibrary.requestAuthorization({status in
if status == .authorized{
let base64String = self.getPhoto(photoAt: photoAt);
call.resolve([
"image": base64String
])
} else {
call.reject("Not authorised to access Photos")
}
})
} else if photos == .authorized {
let base64String = self.getPhoto(photoAt: photoAt);
call.resolve([
"image": base64String
])
} else {
call.reject("Something went wrong");
}
}
func getPhoto(photoAt: Int) -> String {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.fetchLimit = 100
let fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
let image = self.getAssetThumbnail(asset: fetchResult.object(at: photoAt))
let imageData:Data = image.pngData()!
return imageData.base64EncodedString()
}
func getAssetThumbnail(asset: PHAsset) -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: asset, targetSize: CGSize(width: 500, height: 500), contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
}