Getting "Incorrect argument label in call (have 'rest:', expected 'restaurant:')" error. Here is the code. The parameter is correct and I am passing the correct type? Is this because they are class methods?
class func save(restaurant: Restaurant, toCloud: Bool) -> Bool {
var rest:RestaurantMO
var saved:Bool = false
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate) {
rest = RestaurantMO(context: appDelegate.persistentContainer.viewContext)
rest.name = restaurant.name
rest.item = restaurant.item
rest.location = restaurant.location
rest.isVisited = restaurant.isVisited
// Core Data Exercise - Solution
rest.phone = restaurant.phone
let entity =
NSEntityDescription.entity(forEntityName: "Restaurant",
in: appDelegate.persistentContainer.viewContext)!
_ = NSManagedObject(entity: entity,
insertInto: appDelegate.persistentContainer.viewContext)
print("Saving data to context ...")
appDelegate.saveContext()
saved = true
}
if toCloud {
saveRecordToCloud(rest:RestaurantMO) <--- ERROR: Incorrect argument label in call (have 'rest:', expected 'restaurant:')
}
}
class func saveRecordToCloud(restaurant:RestaurantMO!) -> Void {
// Prepare the record to save
let record = CKRecord(recordType: "Restaurant")
record.setValue(restaurant.name, forKey: "name")
record.setValue(restaurant.item, forKey: "item")
record.setValue(restaurant.location, forKey: "location")
record.setValue(restaurant.phone, forKey: "phone")
let imageData = restaurant.image! as Data
// Resize the image
let originalImage = UIImage(data: imageData)!
let scalingFactor = (originalImage.size.width > 1024) ? 1024 / originalImage.size.width : 1.0
let scaledImage = UIImage(data: imageData, scale: scalingFactor)!
// Write the image to local file for temporary use
let imageFilePath = NSTemporaryDirectory() + restaurant.name!
let imageFileURL = URL(fileURLWithPath: imageFilePath)
try? UIImageJPEGRepresentation(scaledImage, 0.8)?.write(to: imageFileURL)
// Create image asset for upload
let imageAsset = CKAsset(fileURL: imageFileURL)
record.setValue(imageAsset, forKey: "image")
// Get the Public iCloud Database
let publicDatabase = CKContainer.default().publicCloudDatabase
// Save the record to iCloud
publicDatabase.save(record, completionHandler: { (record, error) -> Void in
// Remove temp file
try? FileManager.default.removeItem(at: imageFileURL)
})
}
The parameter is correct and I am passing the correct type
No, both are wrong.
The method is declared as
class func saveRecordToCloud(restaurant:RestaurantMO!)
The parameter label is restaurant rather than rest (stated clearly by the error message)
The parameter type is RestaurantMO rather than RestaurantMO.type (an instance is expected)
The correct syntax is
saveRecordToCloud(restaurant: rest)
Note: As the parameter is non-optional remove the exclamation mark in the method declaration:
func saveRecordToCloud(restaurant:RestaurantMO)
Related
I am using CoreData for an app. I have set image as BinaryData in data model. But I have fetched image from server as UIImage and it throws error as:
cannot assign value of type 'UIImage?' to type 'NSData?
I searched but couldn't find any resemblance solution for it. Can anyone help me in swift 3?
My code is:
let url1:URL = URL(string:self.appDictionary.value(forKey: "image") as! String)!
let picture = "http://54.243.11.100/storage/images/news/f/"
let strInterval = String(format:"%#%#",picture as CVarArg,url1 as CVarArg) as String as String
let url = URL(string: strInterval as String)
SDWebImageManager.shared().downloadImage(with: url, options: [],progress: nil, completed: {[weak self] (image, error, cached, finished, url) in
if self != nil {
task.imagenews = image //Error:cannot assign value of type 'UIImage?' to type 'NSData?'
}
})
The error message is pretty clear - you cannot assign UIImage object to a variable of NSData type.
To convert UIImage to Swift's Data type, use UIImagePNGRepresentation
var data : Data = UIImagePNGRepresentation(image)
Note that if you're using Swift, you should be using Swift's type Data instead of NSData
You must convert, image into Data (or NSData) to support imagenews data type.
Try this
let url1:URL = URL(string:self.appDictionary.value(forKey: "image") as! String)!
let picture = "http://54.243.11.100/storage/images/news/f/"
let strInterval = String(format:"%#%#",picture as CVarArg,url1 as CVarArg) as String as String
let url = URL(string: strInterval as String)
SDWebImageManager.shared().downloadImage(with: url, options: [],progress: nil, completed: {[weak self] (image, error, cached, finished, url) in
if self != nil {
if let data = img.pngRepresentationData { // If image type is PNG
task.imagenews = data
} else if let data = img.jpegRepresentationData { // If image type is JPG/JPEG
task.imagenews = data
}
}
})
// UIImage extension, helps to convert Image into data
extension UIImage {
var pngRepresentationData: Data? {
return UIImagePNGRepresentation(img)
}
var jpegRepresentationData: Data? {
return UIImageJPEGRepresentation(self, 1.0)
}
}
I've a CKRecord type created in the CloudKit backend with some properties related to that class.
I've String properties, Bytes and I have a Asset List property, so store some images (multiple images related to a single record).
Now I'm trying so store some images and then fill the property and then trying to save it to CloudKit, but it's not working.
Code goes as it follows:
var images_array = [CKAsset]()
// append the an image to the array
images_array.append(CKAsset(fileURL: writeImage(image: selectedImage) as URL))
let record = CKRecord(recordType: recordName)
record["class_title"] = someString as CKRecordValue
record["class_body"] = someString as CKRecordValue
record["images_array"] = images_array as CKRecordValue
saveRecord(record)
func saveRecord(_ xrecord: CKRecord) {
let publicData = CKContainer.default().publicCloudDatabase
let record: [CKRecord] = [xrecord]
let saveOperation = CKModifyRecordsOperation.init(recordsToSave: record, recordIDsToDelete: nil)
saveOperation.perRecordCompletionBlock = {(record, error) -> Void in
if (error != nil) {
print("error")
}
}
publicData.add(saveOperation)
}
func writeImage(image: UIImage) -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = NSURL(fileURLWithPath: documentsURL.absoluteString).appendingPathComponent(".jpg")
if let imageData = image.lowestQualityJPEGNSData {
do {
try imageData.write(to: fileURL!)
} catch {
print("ERRO 001 = \(error.localizedDescription)")
}
}
return fileURL!
}
extension UIImage {
var uncompressedPNGData: Data? { return UIImagePNGRepresentation(self) }
var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0) }
var highQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.75) }
var mediumQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.5) }
var lowQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.25) }
var lowestQualityJPEGNSData:Data? { return UIImageJPEGRepresentation(self, 0.0) }
}
If I only save the strings, everything works perfectly but with images it doesn't save the record.
I know there might be any issue with the appending, or I have to save the array in other way, or I shouldn't save it as CKRecordValue.
Do you have any tip on how to achieve this?
Thanks
When you create your local asset file you should do so with the atomic write option. This will ensure that the file is completely written before CloudKit attempts to upload the asset.
This is the asset file creation function I use in the Seam 3 library:
fileprivate func createAsset(data: Data) -> CKAsset? {
var returnAsset: CKAsset? = nil
let tempStr = ProcessInfo.processInfo.globallyUniqueString
let filename = "\(tempStr)_file.bin"
let baseURL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = baseURL.appendingPathComponent(filename, isDirectory: false)
do {
try data.write(to: fileURL, options: [.atomicWrite])
returnAsset = CKAsset(fileURL: fileURL)
} catch {
print("Error creating asset: \(error)")
}
return returnAsset
}
You have to take Array of CKAsset for images.
var imageUrls = [CKAsset]()
Now get all images using for-loop. And save CKAsset of images.
for images in self.arrayImageSelected{
var myImage = UIImage()
if (images.isKindOfClass(PHAsset)){
let imageC = images as? PHAsset
myImage = self.getAssetThumbnail(imageC!)
}else if (images.isKindOfClass(UIImage)){
myImage = (images as? UIImage)!
}
let imagePath = self.storeImageAtDocumentDirectory(myImage, titleName: self.strTitle)
myPAth.append(imagePath)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let FbPath = paths.stringByAppendingString("/Custom")
let filePathToWrite = "\(FbPath)" + imagePath
let urls = NSURL(fileURLWithPath: filePathToWrite)
let imageAsset = CKAsset(fileURL: urls)
imageUrls.append(imageAsset)
}
Set Your array.
record.setObject(imageUrls, forKey: "images_array")
For some reason I can't figure out how to save images to core data and fetch them again. I have a feeling it's something about my types but have a look:
I get my data from an api call to my server. It returns a base64 string.
Here is where I get the data:
updateAccessTokenOnly(newAccessToken: aToken!)
saveImageToDB(brandName: imageBrandName, image: data! )
Here I save it to my DB:
func saveImageToDB(brandName: String, image: Data) {
dropImages(){tableDropped in
let managedContext = getContext()
let entity = NSEntityDescription.entity(forEntityName: "CoffeeShopImage", in: managedContext)!
let CSI = NSManagedObject(entity: entity, insertInto: managedContext)
CSI.setValue(image, forKey: "image")
CSI.setValue(brandName, forKey: "brandName")
do {
try managedContext.save()
print("saved!")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
then to fetch it:
func getImageFromDB(callback: #escaping (_ image: UIImage)-> ()) {
let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: "CoffeeShopImage")
do {
let searchResults = try getContext().fetch(fetchRequest)
for images in searchResults {
print("vi når her ned i get image")
if (images.value(forKey: "brandName")! as! String == "Baresso"){
print(images.value(forKey: "brandName")! as! String)
let image: Data = images.value(forKey: "image")! as! Data
let decodedimage = UIImage(data: image)
callback(decodedimage!)
}
}
} catch {
print("Error with request: \(error)")
}
}
Full error log:
https://docs.google.com/document/d/1gSXE64Sxtzo81eBSjv4bnBjBnnmG4MX2tuvNtnuJDIM/edit?usp=sharing
Hope someone can help. Thanks in advance!
UPDATED
So I uninstalled the app and then the code above worked. However the pictures come out blue? (yes I've checked that the pictures sent from the database are correct).
Any solution?
replace
let image: Data = images.value(forKey: "image")! as! Data
let dataDecoded : Data = Data(base64Encoded: image, options: [])!
let decodedimage = UIImage(data: dataDecoded)
with
let image: Data = images.value(forKey: "image")! as! Data
let decodedimage = UIImage(data: image)
Base64 is a way to to convert data to a string. There is no reason to use it here. You already have the data from the database you just want to convert it to a UIImage.
also change
let image = data?.base64EncodedData()
saveImageToDB(brandName: imageBrandName, image: image!)
to
saveImageToDB(brandName: imageBrandName, image: data!)
base64EncodedData is turning the data from image data into a utf-8 encoded based64encoded string. There is no reason for that.
You should get the base64 encoded string from server, convert it to data and then you never need base64 again. Read and write data to your database, and after you read it convert it to a UIImage. Base64 is an encoding method to transfer data. If you are not talking to the server there is no reason to use base64.
After the suggested corrections from Jon Rose all I needed was to add
.withRenderingMode(.alwaysOriginal)
to where I was showing my picture and the code worked.
Saving Image:
guard let managedObjectContext = managedObjectContext else { return }
// Create User
let user = User(context: managedObjectContext)
// Configure User
user.name = "name"
user.about = "about"
user.address = "Address"
user.age = 30
if let img = UIImage(named: "dog.png") {
let data = img.pngData() as NSData?
user.image = data
}
Fetching Image:
// Create Fetch Request
let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let users = try! managedContext.fetch(fetchRequest)
let user: User = users.first as! User
if let imageData = user?.image {
imgView.image = UIImage(data: imageData as Data)
}
I'm hitting Facebook's graph to pull basic user info when the user logs in. My question is how do I use swift to save/pull that information in the best way so that it persists across the child viewcontrollers thereafter (basically everything after login). For instance, I want to use the profile pic as a settings button throughout the app after the login screen (not in it) in my login view controller I have this relevant code:
let userImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
return label
}()
and then later:
func fetchProfile() {
let parameters = ["fields": "email, first_name, last_name, picture.type(large)"]
FBSDKGraphRequest(graphPath: "me", parameters: parameters).startWithCompletionHandler({ (connection, user, requestError) -> Void in
if requestError != nil {
print(requestError)
return
}
var _ = user["email"] as? String
let firstName = user["first_name"] as? String
let lastName = user["last_name"] as? String
self.nameLabel.text = "\(firstName!) \(lastName!)"
var pictureUrl = ""
if let picture = user["picture"] as? NSDictionary, data = picture["data"] as? NSDictionary, url = data["url"] as? String {
pictureUrl = url
}
let url = NSURL(string: pictureUrl)
NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
return
}
let image = UIImage(data: data!)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.userImageView.image = image
})
}).resume()
})
}
What do I need to do to access this in my second ViewController? From what I can understand, segues only help if I have a physical attribute in the first viewController to push them from.
Thanks
The best way to save images will be with Documents Directory as Core Data is not optimized for files as large as images. You would want to save the photo in Documents Directory as so......
func saveImageDocumentDirectory(){
let fileManager = NSFileManager.defaultManager()
let paths = (NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString).stringByAppendingPathComponent(*** Name of DocDir Image***)
let image = // *** Your Facebook Image ***
print(paths)
let imageData = UIImageJPEGRepresentation(image!, 0.5)
fileManager.createFileAtPath(paths as String, contents: imageData, attributes: nil)
}
Then in your viewcontroller(s) create an empty public image var fbImage:UIImage() then create a getImage function and code as follows.....
func getImage()
{
let fileManager = NSFileManager.defaultManager()
let imagePAth = (self.getDirectoryPath() as NSString).stringByAppendingPathComponent(*** Name of Your DocDir Image ***)
if fileManager.fileExistsAtPath(imagePath){
self.fbImage.image = UIImage(contentsOfFile: imagePath)
}else{
print("No Image Saved")
}
}
I'm creating QR Codes in my app and I'm wanting to know if it's possible to add a second key-value pair. Right now I have a 12 digit number for the "inputMessage" key and I'm wanting to have some other data for another key. For example, using another string for a new key called "gym".
Here is my function for creating the QR Code:
func generateCode() {
let gymData = gymName.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)
let codeNumberData = generateRandomNumber(12).dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(codeNumberData, forKey: "inputMessage")
// filter?.setValue(gymData, forKey: "gym")
filter?.setValue("Q", forKey: "inputCorrectionLevel")
let qrCodeImage = filter?.outputImage
let context = CIContext(options: nil)
let cgImage = context.createCGImage(qrCodeImage!, fromRect: (qrCodeImage?.extent)!)
let image = UIImage(CGImage: cgImage, scale: 1.0, orientation: .Up)
let resized = resizeImage(image, withQuality: CGInterpolationQuality.None, rate: 5.0)
codeImageView.image = resized
}
The CIFilter is only expecting to generate the QR code from inputMessage, so you need to create a single aggregate inputMessage and pass that to the filter. One fairly straight forward way of doing this is to create a Dictionary from your inputs, serialize it into a NSData blob using the NSKeyedArchiver, and then set the result as your inputMessage.
func generateCode() {
var aggregateData = [String: NSData]()
if let gymData = gymName.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false) {
aggregateData.updateValue(gymData, forKey: "gymData")
}
if let codeNumberData = generateRandomNumber(12).dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false) {
aggregateData.updateValue(codeNumberData, forKey: "codeNumberData")
}
let archived = NSKeyedArchiver.archivedDataWithRootObject(aggregateData)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(archived, forKey: "inputMessage")
filter?.setValue("Q", forKey: "inputCorrectionLevel")
let qrCodeImage = filter?.outputImage
let context = CIContext(options: nil)
let cgImage = context.createCGImage(qrCodeImage!, fromRect: (qrCodeImage?.extent)!)
let image = UIImage(CGImage: cgImage, scale: 1.0, orientation: .Up)
let resized = resizeImage(image, withQuality: CGInterpolationQuality.None, rate: 5.0)
codeImageView.image = resized
}
Of course this means that on the receiving end, you'll need to expect the payload to be a dictionary, and access the individual components by their keys. Should look something like this.
guard let inputData = scannedQrString.dataUsingEncoding(NSISOLatin1StringEncoding, allowLossyConversion: false),
dictionary = NSKeyedUnarchiver.unarchiveObjectWithData(inputData) as? [String: NSData] else {
return
}
let gymData = dictionary["gymData"]
let codeNumberData = dictionary["codeNumberData"]