Here is my problem, I'm trying to make a clone of Snapchat, a very simple one, using Parse.com with the default project given in the page, everything was going ok, until I have to take pictures and save them to send them to an user. With this code:
import UIKit
import Parse
class UsersTableViewController: UITableViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var usersArray: [String] = []
var activeRecipient: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
var query = PFUser.query()
query?.whereKey("username", notEqualTo: PFUser.currentUser()!.username!)
var users = query?.findObjects()
if let user = users {
for username in user {
println(username.username!!)
usersArray.append(username.username!!)
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return usersArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("snapCell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = usersArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
activeRecipient = indexPath.row
pickImage()
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
//Upload image to parse //error come somewhere from here I think
println("Image Selected")
self.dismissViewControllerAnimated(true, completion: nil)
var imageToSend = PFObject(className:"Image")
imageToSend["image"] = UIImageJPEGRepresentation(image, 0.5)
imageToSend["senderUsername"] = PFUser.currentUser()!.username
imageToSend["recipientUsername"] = usersArray[activeRecipient]
imageToSend.save()
}
func pickImage() {
var image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
image.allowsEditing = false
self.presentViewController(image, animated: true, completion: nil)
}
}
The error:
2015-06-24 15:03:13.414 SnapClomSwift[2043:124661] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableData PF_base64EncodedString]: unrecognized selector sent to instance 0x7a648960'
Is not very helpful to debug, any help??
EDIT1: I would think the parse function is the last calling and breaking everything but I'm not sure.
EDIT2: I have I guess fixed but I'm not sure what was in the first place, still. The new code is this:
var imageToSend = PFObject(className:"Image")
//var imageData = UIImagePNGRepresentation(image)
imageToSend["senderUsername"] = PFUser.currentUser()!.username!
imageToSend["recipientUsername"] = usersArray[activeUser]
imageToSend.saveInBackgroundWithBlock({
(success: Bool, error: NSError?) -> Void in
if (success == false) {
// Error.
println("Error horray! \(error?.description)")
} else {
// There was a problem, check error.description
let imageData = UIImagePNGRepresentation(image)
let imageFile = PFFile(name: "image-png", data: imageData)
imageToSend["image"] = imageFile
imageToSend.saveInBackgroundWithBlock({
(success: Bool, error: NSError?) -> Void in
if success == false {
println("something is fucked up \(error?.description)")
} else {
println("Cool")
}
})
println("Done")
}
})
I guess this is not a solution but a workaround so I'm going to accept Zaph's answer.
To see the actual statement that is causing the error add an exception breakpoint:
From the Mian Menu Debug:Breakpoints:Create Exception Breakpoint.
Right-click the breakpoint and set the exception to Objective-C.
Add an action: "po $arg1".
Run the app to get the breakpoint and you will be at the line that causes the exception and the error message will be in the debugger console.
Related
I have a tableView with multiple sections and i want to show in a cell (via notification) the progress of a download that is being handle by Alamofire.
Right now, i already have the notification post working and passing as info, an episode object, like this:
let info = ["episode": episode, "progress": progress.fractionCompleted] as [String : Any]
NotificationCenter.default.post(name: .downloadProgress, object: nil, userInfo: info)
Each of cells have an episode object. So i want to find the IndexPath of a cell that have an episode object that matches with the episode object that is being passed from a notification.
I can't figure out how can loop through my cells to find which one have that episode and get it's indexPath so i can respond to the notification properly.
I tried to get the index of the array that is being dataSource but as the tableView has multiple sections, this is not working.
Can someone help me? Thanks
My TableViewController:
//
// EpisodesViewController.swift
// Podee
//
// Created by Vinícius Barcelos on 21/07/18.
// Copyright © 2018 Vinícius Barcelos. All rights reserved.
//
import UIKit
import RealmSwift
import Kingfisher
class EpisodesTableViewController: UITableViewController {
//MARK:- Variables
var episodes: Results<Episode> = RealmService.shared.read(object: Episode.self).sorted(byKeyPath: "pubDate", ascending: true)
let episodesCellId = "episodesCellId"
var notificationToken: NotificationToken?
var episodesDictionary = Dictionary<Date, [Episode]>()
var dateDays = [Date]()
//MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupObservers()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
deinit {
self.notificationToken?.invalidate()
//NotificationCenter.default.removeObserver(self, name: NSNotification.Name.downloadProgress, object: nil)
}
//MARK:- Setup
fileprivate func setupObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleDownloadProgressNotification(notification:)), name: .downloadProgress, object: nil)
}
}
#objc func handleDownloadProgressNotification(notification:Notification) {
////////
}
//MARK:- Tableview methods
override func numberOfSections(in tableView: UITableView) -> Int {
return episodesDictionary.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let key = dateDays[section]
guard let datesValues = episodesDictionary[key] else {
return 0
}
return datesValues.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMMM"
return dateFormatter.string(from: dateDays[section])
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: episodesCellId, for: indexPath) as! EpisodesTableViewCell
let key = dateDays[indexPath.section]
if let podcastValues = episodesDictionary[key] {
cell.delegate = self
cell.progressBar.isHidden = true
cell.episode = podcastValues[indexPath.row]
}
return cell
}
}
Download code:
// Start download
Alamofire.request(episode.streamURL).downloadProgress { (progress) in
// Send a notification about the download progress
let info = ["episode": episode, "progress": progress.fractionCompleted] as [String : Any]
NotificationCenter.default.post(name: .downloadProgress, object: nil, userInfo: info)
//print(progress)
// Check data
}.responseData { (responseData) in ......
Modify your function of download and add the following parameters
func downloadFile(url: String,date: Date, index: Int){
let utilityQueue = DispatchQueue.global(qos: .utility)
Alamofire.download(url)
.downloadProgress(queue: utilityQueue) { progress in
let info: [String: AnyHashable] = ["date": date,
"index" : index,
"progress": progress.fractionCompleted
]
NotificationCenter.default.post(name: .downloadProgress, object: nil, userInfo: info)
}
.responseData { response in
......
}
}
In your viewcontroller, replace the function with following code:
#objc func handleDownloadProgressNotification(notification:Notification) {
var dateDays = [Date]()
guard let info = notification.userInfo,
let date = info["date"] as? Date,
let index = info["index"] as? Int,
let progress = info["progress"] as? Double,
let section = dateDays.index(where: {$0 == date})
else {return}
let indexPath = IndexPath(item: index, section: section)
}
In the download function we are passing the date and index of the row from where you started the download and you are returning it back with notification. you can also send section and row index to download function. it's mainly upto you how you want to track the row. you could've also set delegate instead of notification to track the download progress
When I try to reload tableview I get an error like
libc++abi.dylib: terminating with uncaught exception of type NSException
My UITableViewController
class NewTableViewController: UITableViewController {
var videos = [Video]()
{
didSet {
self.tableView.reloadData() //error here
}
}
let URL = "https://api.vid.me/channel/1/new"
override func viewDidLoad() {
super.viewDidLoad()
let request = Alamofire.request(.GET, self.URL)
request.responseObject { (response: Response<NewestVideos, NSError>) in
switch response.result {
case .Success(let newest):
self.videos = newest.videos!
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return self.videos.count}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! NewTableViewCell
let video = videos[indexPath.row]
cell.newVideoNameLabel?.text = video.completeUrl != nil ? video.completeUrl! : "nil"
return cell
}
}
I think it's problem with threading?
In my app I make a .get request and I get a value if I try to print in didSet like print(self.videos)
I tried adding
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
The libc++abi.dylib: terminating with uncaught exception of type NSException error, happens from any ways.
Remove reference from outlet
Bad name in performSegueWithIdentifier
IBActions type.
Check this link for more info
Make sure when you set videos, you are calling it in the Main Queue
You can protect videos when you set in on Main Queue:
dispatch_async(dispatch_get_main_queue()) {
self.videos = newest.videos!
}
Or you can protect it under:
didSet {
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
I am trying to pull an array of strings in the from "my_classes" in the "User" class in Parse. I want each individual string within the array to become a separate cell in a tableview when I tap on the search button. This is my array in "my_classes" : ["Physics","Economics","Pre Calculus"]. I want "Physics" as it's own cell, "Economics" as its own cell, etc.
import UIKit
import Parse
class CardSetClassTableViewController: UITableViewController, UISearchBarDelegate {
// MARK: Outlets
#IBOutlet var searchBar: UISearchBar!
#IBOutlet var resultsTableView: UITableView!
// MARK: Variables
var searchResults = [String]()
// MARK: Actions
#IBAction func newClassBarButtonItemPressed(sender: AnyObject) {
self.performSegueWithIdentifier("newClassSegue", sender: self)
}
// MARK: Functions
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle:UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
if reachabilityStatus == kNOTREACHABLE {
self.displayAlert("No Internet Connection", message: "Please connect to the internet before continuing.")
} else {
searchBar.resignFirstResponder()
print("Search word = \(searchBar.text!)")
let classNameQuery = PFQuery(className:"_User")
classNameQuery.whereKey("my_classes".lowercaseString, equalTo: searchBar.text!.lowercaseString)
let query = PFQuery.orQueryWithSubqueries([classNameQuery])
query.findObjectsInBackgroundWithBlock {
(results: [PFObject]?, error: NSError?) -> Void in
if error != nil {
self.displayAlert("Error", message: error!.localizedDescription)
return
}
if let objects = results {
self.searchResults.removeAll(keepCapacity: false)
for object in objects {
let className = object.valueForKey("my_classes") as! String
self.searchResults.append(className)
}
dispatch_async(dispatch_get_main_queue()) {
self.resultsTableView.reloadData()
self.searchBar.resignFirstResponder()
}
}
}
}
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel!.text = searchResults[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let classIndexPath = tableView.indexPathForSelectedRow!
let selectedCell = tableView.cellForRowAtIndexPath(classIndexPath)! as UITableViewCell
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Loading"
if reachabilityStatus == kNOTREACHABLE {
spinningActivity.hide(true)
self.displayAlert("No Internet Connection", message: "Please connect to the internet before continuing.")
} else {
// let className : String = String(selectedCell.textLabel!.text!)
self.performSegueWithIdentifier("addCardSet", sender: self)
}
searchBar.resignFirstResponder()
}
}
Thanks!
Try the following...
Edit
var songsArray = [String]()
func fetchUsers() {
let userQuery: PFQuery = PFUser.query()!
//search users by the sepcified username, returns a users! object
//make an array to put the values from the users! array object into
//then append those from your "middle-man" array into your destination array,
//in this example songArray is destination array and songsFromParse is "middle-man" array
userQuery.whereKey("username", equalTo: (username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var songsFromParse = users!
if error == nil {
if songsFromParse.count != 0 {
self.songsArray = (songsFromParse[i].valueForKey("CurrentSongURLArray") as! Array)
}
self.tableView.reloadData()
} else {
print(error)
}
})
}
You then take your new array that contains the objects that you retrieved, in this example songsArray and use it to populate your tableView. In cellForRowAtIndexPath ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell ID")
cell?.textLabel?.text = songsArray[indexPath]
return cell!
}
I am new to Core Data and I have encountered a problem. This app is supposed to store images from the photo library to core data and display those in a collection view.
However, the problem is the pictures display when the app is newly installed on the simulator and you just add pictures. But when you close the app and open again it crashes and shows an error in the console: Thread 1 EXC_BAD_ACCESS
// Loading Setup
#IBOutlet var collection: UICollectionView!
var images = [NSManagedObject]()
override func viewDidAppear(animated: Bool) {
let managedContext = AppDelegate().managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "FullRes")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
images = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
collection.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return images.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! MainCollectionViewCell
let selectedObject = images[indexPath.row]
print(selectedObject)
let image: UIImage = UIImage(data: selectedObject.valueForKey("imageData") as! NSData!)!
cell.imageView.image = image
// Configure the cell
return cell
}
// MARK: UICollectionViewDelegate
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
override func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
override func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
override func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
override func collectionView(collectionView: UICollectionView, canPerformAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool {
return false
}
override func collectionView(collectionView: UICollectionView, performAction action: Selector, forItemAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) {
}
*/
// Camera Setup
// the image picker
let imagePicker = UIImagePickerController()
// a queue to save the image without freezing the App UI
let saveQueue = dispatch_queue_create("saveQueue", DISPATCH_QUEUE_CONCURRENT)
// the Managed Object Context where we will save our image to.
let managedContext = AppDelegate().managedObjectContext
#IBAction func imageDidPress(sender: AnyObject) {
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
presentViewController(imagePicker, animated: true, completion: nil)
}
#IBAction func addDidPress(sender: AnyObject) {
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
prepareImageForSaving(image)
self.dismissViewControllerAnimated(true, completion: nil)
}
/*
func deletePhotoFromLibrary(info: [String : AnyObject]) {
print("wasrun")
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageUrls = [imageUrl]
//Delete asset
PHPhotoLibrary.sharedPhotoLibrary().performChanges( {
let imageAssetToDelete = PHAsset.fetchAssetsWithALAssetURLs(imageUrls, options: nil)
PHAssetChangeRequest.deleteAssets(imageAssetToDelete)
},
completionHandler: { success, error in
NSLog("Finished deleting asset. %#", (success ? "Success" : error!))
})
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
}
*/
func prepareImageForSaving(image:UIImage) {
// use date as unique id
let date : Double = NSDate().timeIntervalSince1970
// dispatch with gcd.
dispatch_async(saveQueue) {
// create NSData from UIImage
guard let imageData = UIImageJPEGRepresentation(image, 1) else {
// handle failed conversion
print("jpg error")
return
}
// scale image
let thumbnail = self.scale(image: image, toSize: self.view.frame.size)
guard let thumbnailData = UIImageJPEGRepresentation(thumbnail, 0.7) else {
// handle failed conversion
print("jpg error")
return
}
// send to save function
self.saveImage(imageData, thumbnailData: thumbnailData, date: date)
}
}
func saveImage(imageData:NSData, thumbnailData:NSData, date: Double) {
dispatch_barrier_async(saveQueue) {
// create new objects in moc
guard let fullRes = NSEntityDescription.insertNewObjectForEntityForName("FullRes", inManagedObjectContext: self.managedContext) as? FullRes, let thumbnail = NSEntityDescription.insertNewObjectForEntityForName("Thumbnail", inManagedObjectContext: self.managedContext) as? Thumbnail else {
// handle failed new object in moc
print("moc error")
return
}
//set image data of fullres
fullRes.imageData = imageData
//set image data of thumbnail
thumbnail.imageData = thumbnailData
thumbnail.id = date as NSNumber
//set relationship between the two objects
thumbnail.fullRes = fullRes
// save the new objects
do {
try self.managedContext.save()
} catch {
// implement error handling here
fatalError("Failure to save context: \(error)")
}
// clear the moc
self.managedContext.refreshAllObjects()
}
}
func scale(image image:UIImage, toSize newSize:CGSize) -> UIImage {
// make sure the new size has the correct aspect ratio
let aspectFill = resizeFill(image.size, toSize: newSize)
UIGraphicsBeginImageContextWithOptions(aspectFill, false, 0.0);
image.drawInRect(CGRectMake(0, 0, aspectFill.width, aspectFill.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
func resizeFill(fromSize: CGSize, toSize: CGSize) -> CGSize {
let aspectOne = fromSize.height / fromSize.width
let aspectTwo = toSize.height / toSize.width
let scale : CGFloat
if aspectOne < aspectTwo {
scale = fromSize.height / toSize.height
} else {
scale = fromSize.width / toSize.width
}
let newHeight = fromSize.height / scale
let newWidth = fromSize.width / scale
return CGSize(width: newWidth, height: newHeight)
}
Update: Based on the comments made below, I have made changes to the code.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! MainCollectionViewCell
if let imagePath = images[indexPath.row].imageData {
cell.imageView.image = UIImage(data: imagePath)
}
// Configure the cell
return cell
}
But now, I get another error: 2015-12-26 20:10:24.464 Collect[480:69113] -[Thumbnail imageData]: unrecognized selector sent to instance 0x13fe9ba00.
The code class AppDelegate: UIResponder, UIApplicationDelegate { on AppDelegate.swift is highlighted.
It is an interesting thing that the app works until a certain point. For instance, when I uninstall the app from the phone I am running it in and then rerun it again, it works for a while until it crashes.
But still, it does not work perfectly like I hope it would be. Sometimes, one picture will appear only if another picture is added.
Update 2: I made some adjustments based on your comments below:
override func viewDidAppear(animated: Bool) {
authenticateUser()
let managedContext = AppDelegate().managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Thumbnail")
managedContext.performBlockAndWait { () -> Void in
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
self.images = results as! [Thumbnail]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
collection.reloadData()
}
It works fine whenever I delete the app and rerun it. However, when the app is closed, it no longer has the capability to restore the images.
I still get -[Thumbnail imageData]: unrecognized selector sent to instance 0x13762d650 while running it. The highlighted code is class AppDelegate: UIResponder, UIApplicationDelegate { on AppDelegate.swift
You are right, there is a mistake in my answer on the other question.
CoreData needs all operation in one NSManagedObjectContext to happen on the same thread as the one on which it was created. I updated the answer.
Here is the relevant code :
Create two separate queue's in the UIViewController
let convertQueue = dispatch_queue_create("convertQueue", DISPATCH_QUEUE_CONCURRENT) // this one is for image stuff
let saveQueue = dispatch_queue_create("saveQueue", DISPATCH_QUEUE_CONCURRENT) // this one is for CoreData
Set value of moc on the correct thread
extension ViewController {
// call this in viewDidLoad
func coreDataSetup() {
dispatch_sync(saveQueue) {
self.managedContext = AppDelegate().managedObjectContext
}
}
}
Save images with :
extension ViewController {
func saveImage(imageData:NSData, thumbnailData:NSData, date: Double) {
dispatch_barrier_sync(saveQueue) {
// create new objects in moc
guard let moc = self.managedContext else {
return
}
guard let fullRes = NSEntityDescription.insertNewObjectForEntityForName("FullRes", inManagedObjectContext: moc) as? FullRes, let thumbnail = NSEntityDescription.insertNewObjectForEntityForName("Thumbnail", inManagedObjectContext: moc) as? Thumbnail else {
// handle failed new object in moc
print("moc error")
return
}
//set image data of fullres
fullRes.imageData = imageData
//set image data of thumbnail
thumbnail.imageData = thumbnailData
thumbnail.id = date as NSNumber
thumbnail.fullRes = fullRes
// save the new objects
do {
try moc.save()
} catch {
fatalError("Failure to save context: \(error)")
}
// clear the moc
moc.refreshAllObjects()
}
}
}
Okay so I got my ActivityViewController to work fine when it is sharing like normal however whenever a user hits cancel after they invoked the ActivityViewController , I get the annoying fatal error.
fatal error: unexpectedly found nil while unwrapping an Optional value
So it looks like I did not unwrap my optionals well. Here's the share method :
#IBAction func share(sender: UIBarButtonItem) {
var memeedimage = generateMemedImage()
let activityViewController = UIActivityViewController(activityItems:[memeedimage] , applicationActivities: nil)
presentViewController(activityViewController, animated: true, completion: nil)
activityViewController.completionWithItemsHandler = {
(activity, success, returneditems, error) in
println("Activity: \(activity) Success: \(success) Items: \(returneditems) Error: \(error)")
self.save()
activityViewController.dismissViewControllerAnimated(true, completion:{
let memevc:MemeTableViewController = MemeTableViewController()
self.presentViewController(memevc, animated: true, completion: nil)
})
}
and the share method calls the save function which generates the object called meme using an implicitly unwrapped optional which causes the error :
func save(){
var meme : MemeObject!
meme = MemeObject(textFieldtop : texfieldtop.text! ,textFieldbottom : textfieldbottom.text! ,image : imagePickerView.image! , memedImage : generateMemedImage())
(UIApplication.sharedApplication().delegate as! AppDelegate).memes.append(meme!)
}
So I decided to safely unwrap the meme optional value but that invoked another problem
func save(){
var meme : MemeObject?
if let memez = meme{
meme = MemeObject(textFieldtop : texfieldtop.text! ,textFieldbottom : textfieldbottom.text! ,image : imagePickerView.image! , memedImage : generateMemedImage())
(UIApplication.sharedApplication().delegate as! AppDelegate).memes.append(meme!)
}
else{
println("Optionals man")
}
}
Now when the the object is not nil , "Optionals man" is printed which shouldn't happen and the completionwithitemshandler property in the share method didn't present the table view controller which should happen directly after the user shares the object.
Code for MemeTableViewController :
import UIKit
class MemeTableViewController : UIViewController,UITableViewDelegate,UITableViewDataSource
{
var memesz: [MemeObject]!
#IBOutlet var tableView: UITableView!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
memesz = appDelegate.memes
//tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.hidesBarsOnSwipe = true
navigationController?.hidesBarsOnTap = true
}
//reserves the number of rows needed to display the image
func tableView(tableView : UITableView, numberOfRowsInSection section : Int)->Int
{
return memesz.count
}
//Reserves the row to be dequeued for display
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewzCell = tableView.dequeueReusableCellWithIdentifier("MemesCell", forIndexPath: indexPath) as! TableViewzCell
let memezrow = memesz[indexPath.row]
cell.label1.text = memezrow.textFieldtop
cell.label2.text = memezrow.textFieldbottom
cell.imageview.image = memezrow.memedImage
return cell
}
//Method to do something when the row is selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let detailController = self.storyboard!.instantiateViewControllerWithIdentifier("FullScreenMeme") as! FullScreenMeme
detailController.meme = memesz[indexPath.row]
self.navigationController!.pushViewController(detailController, animated: true)
}
}
Any help please?
After looking your code, it seems like you haven't initialised the variable 'meme'.
func save(){
var meme : MemeObject? = MemeObject(textFieldtop : texfieldtop.text! ,textFieldbottom : textfieldbottom.text! ,image : imagePickerView.image! , memedImage : generateMemedImage())
if let memez = meme{
(UIApplication.sharedApplication().delegate as! AppDelegate).memes.append(meme!)
}
else{
println("Optionals man")
}
}
}
#IBAction func share(sender: UIBarButtonItem) {
var memeedimage = generateMemedImage()
let activityViewController = UIActivityViewController(activityItems:[memeedimage] , applicationActivities: nil)
presentViewController(activityViewController, animated: true, completion: nil)
activityViewController.completionWithItemsHandler = {
(activity, success, returneditems, error) in
println("Activity: \(activity) Success: \(success) Items: \(returneditems) Error: \(error)")
self.save()
let memevc = self.storyboard!.instantiateViewControllerWithIdentifier("MemeTableViewConroller") as! MemeTableViewController
self.presentViewController(memevc, animated: true, completion: nil)
}
}