fix
This commit is contained in:
@ -1,8 +1,8 @@
|
|||||||
//
|
//
|
||||||
// ShareViewController.swift
|
// ShareViewController.swift
|
||||||
// Haystack
|
// Haystack
|
||||||
//
|
//
|
||||||
// Created by Rio Keefe on 03/05/2025.
|
// Created by Rio Keefe on 03/05/2025.
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
@ -16,88 +16,156 @@ class ShareViewController: SLComposeServiceViewController {
|
|||||||
let uploadURL = URL(string: "http://192.168.1.199:3040/image/")!
|
let uploadURL = URL(string: "http://192.168.1.199:3040/image/")!
|
||||||
|
|
||||||
var bearerToken: String?
|
var bearerToken: String?
|
||||||
|
// Store the item provider to access it later in didSelectPost
|
||||||
|
private var imageItemProvider: NSItemProvider?
|
||||||
|
private var extractedImageName: String = "image" // Default name
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
// Load the bearer token from the App Group
|
// Load the bearer token from the App Group in viewDidLoad
|
||||||
|
// This is okay as reading from UserDefaults is fast
|
||||||
if let sharedDefaults = UserDefaults(suiteName: appGroupName) {
|
if let sharedDefaults = UserDefaults(suiteName: appGroupName) {
|
||||||
bearerToken = sharedDefaults.string(forKey: tokenKey)
|
bearerToken = sharedDefaults.string(forKey: tokenKey)
|
||||||
print("Retrieved bearer token: \(bearerToken ?? "nil")")
|
print("Retrieved bearer token: \(bearerToken ?? "nil")")
|
||||||
} else {
|
} else {
|
||||||
print("Error accessing App Group UserDefaults.")
|
print("Error accessing App Group UserDefaults.")
|
||||||
|
// Optionally inform the user or disable posting if token is crucial
|
||||||
|
// self.isContentValid() could check if bearerToken is nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the item provider, but don't load the data synchronously yet
|
||||||
if let item = extensionContext?.inputItems.first as? NSExtensionItem,
|
if let item = extensionContext?.inputItems.first as? NSExtensionItem,
|
||||||
let provider = item.attachments?.first as? NSItemProvider {
|
let provider = item.attachments?.first as? NSItemProvider {
|
||||||
if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
|
||||||
let semaphore = DispatchSemaphore(value: 0)
|
self.imageItemProvider = provider
|
||||||
var rawImageData: Data?
|
// Attempt to get a suggested name early if available
|
||||||
var extractedName = "image.png" // Default name
|
extractedImageName = provider.suggestedName ?? "image"
|
||||||
|
if let dotRange = extractedImageName.range(of: ".", options: .backwards) {
|
||||||
|
extractedImageName = String(extractedImageName[..<dotRange.lowerBound])
|
||||||
|
}
|
||||||
|
|
||||||
provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] (item, error) in
|
|
||||||
guard let self = self else {
|
|
||||||
semaphore.signal()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if let url = item as? URL, let data = try? Data(contentsOf: url) {
|
|
||||||
rawImageData = data
|
|
||||||
extractedName = url.lastPathComponent
|
|
||||||
if let dotRange = extractedName.range(of: ".", options: .backwards) {
|
|
||||||
extractedName = String(extractedName[..<dotRange.lowerBound])
|
|
||||||
}
|
|
||||||
} else if let data = item as? Data {
|
|
||||||
rawImageData = data
|
|
||||||
extractedName = provider.suggestedName ?? "image"
|
|
||||||
} else {
|
|
||||||
print("Error loading image data: \(error?.localizedDescription ?? "Unknown error")")
|
|
||||||
self.extensionContext?.cancelRequest(withError: error ?? NSError(domain: "ShareExtension", code: -2, userInfo: nil))
|
|
||||||
}
|
|
||||||
semaphore.signal()
|
|
||||||
}
|
|
||||||
semaphore.wait()
|
|
||||||
|
|
||||||
if let dataToUpload = rawImageData {
|
|
||||||
self.uploadRawData(dataToUpload, imageName: extractedName, bearerToken: self.bearerToken)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
print("No image found.")
|
print("No image found.")
|
||||||
self.extensionContext?.cancelRequest(withError: NSError(domain: "ShareExtension", code: -3, userInfo: nil))
|
// If no image is found, the content is not valid for this extension
|
||||||
|
// You might want to adjust isContentValid() based on this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func uploadRawData(_ rawData: Data, imageName: String, bearerToken: String?) {
|
override func isContentValid() -> Bool {
|
||||||
guard let token = bearerToken else {
|
// Content is valid only if we have an item provider for an image AND a bearer token
|
||||||
print("Bearer token is missing.")
|
return imageItemProvider != nil && bearerToken != nil
|
||||||
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
}
|
||||||
|
|
||||||
|
override func didSelectPost() {
|
||||||
|
// This method is called when the user taps the "Post" button.
|
||||||
|
// Start the asynchronous operation here.
|
||||||
|
|
||||||
|
guard let provider = imageItemProvider else {
|
||||||
|
print("Error: No image item provider found when posting.")
|
||||||
|
// Inform the user or log an error
|
||||||
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard let token = bearerToken else {
|
||||||
|
print("Error: Bearer token is missing when posting.")
|
||||||
|
// Inform the user or log an error
|
||||||
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the image data asynchronously
|
||||||
|
provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] (item, error) in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
print("Error loading image data for upload: \(error.localizedDescription)")
|
||||||
|
// Inform the user about the failure
|
||||||
|
self.extensionContext!.cancelRequest(withError: error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawImageData: Data?
|
||||||
|
var finalImageName = self.extractedImageName // Use the name extracted earlier
|
||||||
|
|
||||||
|
if let url = item as? URL, let data = try? Data(contentsOf: url) {
|
||||||
|
rawImageData = data
|
||||||
|
// Refine the name extraction here if necessary, though doing it in viewDidLoad is also an option
|
||||||
|
finalImageName = url.lastPathComponent
|
||||||
|
if let dotRange = finalImageName.range(of: ".", options: .backwards) {
|
||||||
|
finalImageName = String(finalImageName[..<dotRange.lowerBound])
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if let data = item as? Data {
|
||||||
|
rawImageData = data
|
||||||
|
// Use the suggested name if available, fallback to default
|
||||||
|
finalImageName = provider.suggestedName ?? "image"
|
||||||
|
} else {
|
||||||
|
print("Error: Could not get image data in a usable format.")
|
||||||
|
// Inform the user about the failure
|
||||||
|
self.extensionContext!.cancelRequest(withError: NSError(domain: "ShareExtension", code: -4, userInfo: [NSLocalizedDescriptionKey: "Could not process image data."]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
guard let dataToUpload = rawImageData else {
|
||||||
|
print("Error: No image data to upload.")
|
||||||
|
// Inform the user about the failure
|
||||||
|
self.extensionContext!.cancelRequest(withError: NSError(domain: "ShareExtension", code: -5, userInfo: [NSLocalizedDescriptionKey: "Image data is missing."]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now perform the upload asynchronously
|
||||||
|
self.uploadRawData(dataToUpload, imageName: finalImageName, bearerToken: token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not complete the request here.
|
||||||
|
// The request will be completed in the uploadRawData completion handler.
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadRawData(_ rawData: Data, imageName: String, bearerToken: String) {
|
||||||
|
// bearerToken is guaranteed to be non-nil here due to the guard in didSelectPost
|
||||||
|
|
||||||
let uploadURLwithName = uploadURL.appendingPathComponent(imageName)
|
let uploadURLwithName = uploadURL.appendingPathComponent(imageName)
|
||||||
|
|
||||||
var request = URLRequest(url: uploadURLwithName)
|
var request = URLRequest(url: uploadURLwithName)
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.httpBody = rawData
|
request.httpBody = rawData
|
||||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
||||||
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
request.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
|
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
|
||||||
|
// **IMPORTANT:** Complete the extension request on the main thread
|
||||||
|
DispatchQueue.main.async {
|
||||||
print("Upload finished. Error: \(error?.localizedDescription ?? "None"), Response: \(response?.description ?? "None")")
|
print("Upload finished. Error: \(error?.localizedDescription ?? "None"), Response: \(response?.description ?? "None")")
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
// Handle upload error (e.g., show an alert to the user)
|
||||||
|
print("Upload failed: \(error.localizedDescription)")
|
||||||
|
self?.extensionContext!.cancelRequest(withError: error)
|
||||||
|
} else if let httpResponse = response as? HTTPURLResponse, !(200...299).contains(httpResponse.statusCode) {
|
||||||
|
// Handle non-success HTTP status codes
|
||||||
|
let errorDescription = "Server returned status code \(httpResponse.statusCode)"
|
||||||
|
print(errorDescription)
|
||||||
|
self?.extensionContext!.cancelRequest(withError: NSError(domain: "ShareExtension", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: errorDescription]))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Upload was successful
|
||||||
|
print("Upload successful")
|
||||||
|
// Complete the request when the upload is done
|
||||||
self?.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
self?.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
task.resume()
|
task.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func isContentValid() -> Bool {
|
|
||||||
return true // Always valid for now
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didSelectPost() {
|
|
||||||
// The upload happens in uploadRawData after the data is loaded
|
|
||||||
}
|
|
||||||
|
|
||||||
override func configurationItems() -> [Any]! {
|
override func configurationItems() -> [Any]! {
|
||||||
|
// You can add items here if you want to allow the user to enter additional info
|
||||||
|
// e.g., a text field for a caption.
|
||||||
|
// This example only handles image upload, so no config items are needed.
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user