I need add card to apple Wallet in Unity.
For example - I am already generated pkpass file (it could storage in resources) and now I need only button in my app, what can added this card to Wallet.
I have read this manual and find only code for swift:
guard let passPath = Bundle.main.path(forResource: "wallet", ofType: "pkpass") else { return }
let error: ErrorPointer = ErrorPointer(nilLiteral: ())
guard let passData = NSData(contentsOfFile: passPath) else { return }
let pass = PKPass(data: passData as Data, error: error)
let passLibrary = PKPassLibrary()
passLibrary.addPasses([pass]) { (status) in
print(passLibrary.containsPass(pass))
}
As I understood, I need implement PKPassLibrary in Unity and add this method (with some modifications) to button click event.
Could you help me with this moment, please?
The situation is difficult by the fact that I don't have a Mac and IOs device.
Related
I'm developing Cloud based Chatting app (like Facebook Messenger), In that We want to include feature like auto download (and store) photo-video to Photo Library.
I can store image or video via following code to Photo Library once.
func savePhoto() {
if let assetCollection = fetchAssetCollectionForAlbum() {
PHPhotoLibrary.shared().performChanges {
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
} completionHandler: { (flag, error) in
if error == nil {
HapticHelper.shared.generate(feedbackType: .notificationSuccess)
}
else {
HapticHelper.shared.generate(feedbackType: .notificationError)
}
}
}
}
But When user choose to auto download (and store) photo-video to Photo Library then How can I identify that this image is already stored in library previously?
May some kind of identifier or something else which tells me that from this URL path image is already stored or not.
I have lately been trying to make a widget and wanted to share a piece of data between my widget and my app.
static var sharedDataFileURL: URL {
let appGroupIdentifier = "group.com.unknownstudios.yk"
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
else { preconditionFailure("Expected a valid app group container") }
return url.appendingPathComponent("userID.plist")
}
The above code works fine on my app, but when I run it on my widget it gives me the following output. I have checked that the app group is correct and is active on both the iOS app and the widget.
[unspecified] container_create_or_lookup_path_for_platform: client is not entitled
[unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled
Fatal error: Expected a valid app group container: file WidgetExtension/Library.swift, line 143
Edit:
I have also tried to use UserDefaults, but they don't work either.
The following is the way I use the the FileManager and UserDefaults
UserDefaults(suiteName: "group.com.unknownstudios.yk")!.set("**USERID**", forKey: "userID")
let data = Data("**USERID**".utf8)
do {
try data.write(to: URL.sharedDataFileURL, options: .atomic)
} catch {
print(error.localizedDescription)
}
And the following is how I try to read the data from the widget:
var userID: String? = {
if let userid = UserDefaults(suiteName: "group.com.unknownstudios.yk")!.string(forKey: "userID") {
print(userid)
return userid
} else if let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.unknownstudios.yk") {
if let data = try? Data(contentsOf: url.appendingPathComponent("userID.plist")), let string = String(data: data, encoding: .utf8) {
return string
}
}
return nil
}()
WidgetExtension app groups:
Main app, app groups:
I found out what my problem was. I had accidentally selected the release tab under Signing & Capabilities. Which caused my app group to be wrong when checking on release, thus making the error occur.
I just had to select All and re-add the app groups capability which made it work again.
I am trying to allow users of my app to be able to retrieve the iCloud sharing link to a record that they have ALREADY shared. I can get the URL when creating the share by using the let share = CKShare(rootRecord: CKRecord) followed by Apple's UISharingController. However, every time I use this method, the old link becomes invalid and kicks others out of the share they have already joined. How can I allow the users (the record owners) to fetch a shareable URL for the records CKShare object at any time?
//Initially shares the record
let share = CKShare(rootRecord: newRecord)
share[CKShare.SystemFieldKey.title] = "Group" as CKRecordValue?
let modifyRecordsOperation = CKModifyRecordsOperation( recordsToSave: [newRecord, share], recordIDsToDelete: nil)
//Activates the UISharingController (code not shown)
Assuming the record has already been shared, you can get the URL to invite additional participants with the following:
if let shareReference = record.share {
database.fetch(withRecordID: shareReference.recordID) { (share, error) in
let shareURL = (share as? CKShare)?.url
}
}
If you wanted to show the UICloudSharingController again for an existing share (without it generating a new URL), you could do:
let sharingController = UICloudSharingController { (_, prepareCompletionHandler) in
func createNewShare() {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
let share = CKShare(rootRecord: record)
share[CKShare.SystemFieldKey.title] = "Title" as CKRecordValue
share.publicPermission = .readWrite
let modifyRecordsOp = CKModifyRecordsOperation(recordsToSave: [share, record], recordIDsToDelete: nil)
modifyRecordsOp.modifyRecordsCompletionBlock = { records, recordIDs, error in
prepareCompletionHandler(share, self, error)
}
modifyRecordsOp.database = database
operationQueue.addOperation(modifyRecordsOp)
}
if let shareReference = record.share {
database.fetch(withRecordID: shareReference.recordID) { (share, error) in
guard let share = share as? CKShare else {
createNewShare()
return
}
prepareCompletionHandler(share, self, nil)
}
} else {
createNewShare()
}
}
It sounds like you've got multiple issues going on here. I'd recommend going through the UICloudSharingController documentation carefully.
The first issue is in the code you've provided you're creating a new share each time, when what you want to do is to create it once and then show that share any time you want to interact with it (including getting the share URL). After you create your share you'll probably want to persist that on device somehow (it can be as simple as storing it in UserDefaults).
The second issue is it sounds like you're not instantiating the UICloudSharingController correctly. When you initially create the CKShare you must use the UICloudSharingController init(preparationHandler:) initializer and create the share INSIDE of that.
As per the documentation for the different initializers (emphasis mine):
Important
You must initialize the controller with the correct initializer
method. Do not use init(preparationHandler:) if the CKRecord is
already shared. Likewise, do not use init(share:container:) if the
CKRecord is not shared. Using the wrong initializer leads to errors
when saving the record.
And when you then want to show the shareSheet, you use the UICloudSharingController init(share:container:) initializer, and feed it the share you have persisted to device.
Here's the code provided in the UICloudSharingController documentation for creating the CKShare:
func presentCloudSharingController(_ sender: Any) { guard
let barButtonItem = sender as? UIBarButtonItem,
let rootRecord = self.recordToShare else {
return
}
let cloudSharingController = UICloudSharingController { [weak self] (controller, completion: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
guard let `self` = self else {
return
}
self.share(rootRecord: rootRecord, completion: completion)
}
if let popover = cloudSharingController.popoverPresentationController {
popover.barButtonItem = barButtonItem
}
self.present(cloudSharingController, animated: true) {}
}
func share(rootRecord: CKRecord, completion: #escaping (CKShare?, CKContainer?, Error?) -> Void) {
let shareRecord = CKShare(rootRecord: rootRecord)
let recordsToSave = [rootRecord, shareRecord];
let container = CKContainer.default()
let privateDatabase = container.privateCloudDatabase
let operation = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: [])
operation.perRecordCompletionBlock = { (record, error) in
if let error = error {
print("CloudKit error: \(error.localizedDescription)")
}
}
operation.modifyRecordsCompletionBlock = { (savedRecords, deletedRecordIDs, error) in
if let error = error {
completion(nil, nil, error)
} else {
completion(shareRecord, container, nil)
}
}
privateDatabase.add(operation)
}
And next time you want to show it, it's much more strait forward:
let share = // yourCKShare
let container = // yourCKContainer
let viewController = // yourViewController
let shareController = UICloudSharingController(share: share, container: container)
viewController.present(shareController, animated: true)
And finally to answer the URL part of your question, the UICloudSharingController is used for all things sharing - while the controller is presenting the share, there's a "Copy Link" button available to the owner (and possibly the user as well if you allow public access - I haven't looked at that as my stuff is private):
How to use non-bundle custom fonts in my iOS Application? I want to add font to app via iCloud or from internet and use it inside.
Yes you can do this via
CTFontManagerRegisterGraphicsFont
(https://developer.apple.com/documentation/coretext/1499499-ctfontmanagerregistergraphicsfon)
You only need a reference to a file or data reference. The code should be self-explained:
guard let fontData = NSData(contentsOfFile: pathForResourceString) else {
fatalError("[UIFont] Could not get data of font")
}
guard let dataProvider = CGDataProvider(data: fontData) else {
fatalError("[UIFont] Could not get dataprovider of font")
}
guard let fontRef = CGFont(dataProvider) else {
fatalError("[UIFont] Could not create font")
}
var errorRef: Unmanaged<CFError>?
if CTFontManagerRegisterGraphicsFont(fontRef, &errorRef) == false {
fatalError("[UIFont] Could not register font")
}
Then you can access the font over its name, like it were in the bundle.
Before iOS 11 came out I created a share extension to my social media app. It worked perfectly fine. Once iOS 11 came out, the share extension quit working. I searched and debugged the extension until I found the source of the problem. When looping through the attachments inside the extensionContext.inputItems[0].attachments, none of the attachments has an item conforming to kUTTypeImage. So none of my code was running from that point on. I also had another strange outcome yesterday. This is part of my code inside the didSelectPost function.
guard let content = extensionContext?.inputItems[0] as? NSExtensionItem else { return }
guard let contentAttachments = content.attachments as? [NSItemProvider] else { return }
let skyName = self.textView.text
for attachment in contentAttachments {
if attachment.hasItemConformingToTypeIdentifier(imageType) {
attachment.loadItem(forTypeIdentifier: imageType, options: nil) { (data, error) in
guard error == nil, let url = data as? NSURL else { return }
self.imageFromAsset(url: url as URL)
if !self.selectedType.isEmpty {
do {
let imageData = try Data(contentsOf: url as URL)
self.skyImage = UIImage(data: imageData)
self.saveSkyImage()
guard let skyOriginalImageURL = self.skyOriginalImageURL else { return }
guard let skyImageURL = self.skyImageURL else { return }
let newSky = Sky(name: skyName ?? "Another Sky",
type: self.selectedType,
date: self.date,
location: self.location,
picture: CKAsset(fileURL: skyImageURL),
likes: 0, flags: 0,
likedBy: [CKReference](), flaggedBy: [CKReference](),
originalImage: CKReference(record: CKRecord(recordType: "SkyImage"), action: .none))
let newSkyImage = SkyImageFullResolution(picture: CKAsset(fileURL: skyOriginalImageURL))
self.saveSky(sky: newSky, skyImage: newSkyImage)
} catch {
print(error.localizedDescription)
self.closePostWindow()
}
}
}
}
}
defer {
closePostWindow()
}
I don't have a direct answer to your problem but recently in iOS 11 I have resolved a problem to display a share extension involving PDF files.
My problem was that my expected type identifiers were not found among the attachments.
NSItemProvider has an instance property registeredTypeIdentifiers to show you the type identifiers that can be found when your extension is activated.
That's what I do :
1) I use TRUEPREDICATE as NSExtensionActivationRule to force display my share extension in the context of my interest.
2) After you select your share extension, your extension code will be triggered.
Then you print all the type registeredTypeIdentifiers of each attachment, by looping through your contentAttachments.
Once you have identified all the identifiers, you will be able to find a solution to your problem.