Swift 4save recorded video to photo album - xcode

This is my first attempt at recording and saving a video with AVKit etc. and although I am able to display and record a video with the method:
#IBAction func recordVideo(_ sender: Any) {
// Check the camera is available
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerController.SourceType.camera){
// present the camera
controller.sourceType = .camera
controller.mediaTypes = [kUTTypeMovie as String]
controller.delegate = self
present (controller,animated: true, completion: nil)
}else{
// No camera available so show alert
let alert = UIAlertController(title: "Oops!", message: "Can't access the camera!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Give up", style: .default, handler: nil))
self.present(alert, animated: true)
uploadButton.isHidden = true
}
}
I am having trouble saving the recorded video to the photo album. When I step through the code it jumps from the 'guard' line into the 'else' block and simply returns. So for some reason it's not able to assign anything to 'mediaType'
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo vidInfo: [UIImagePickerController.InfoKey : Any]){
dismiss(animated: true, completion: nil)
guard let mediaType = vidInfo[UIImagePickerController.InfoKey.mediaURL] as? String,
mediaType == (kUTTypeMovie as String),
let url = vidInfo[UIImagePickerController.InfoKey.mediaURL] as? URL,
UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(url.path)
else {
return
}
// Handle a movie capture
UISaveVideoAtPathToSavedPhotosAlbum(url.path, self, nil, nil)
}
Any help would be appreciated.

Related

TableView not updating

I'm currently struggling with getting my TableView to update after I finish performing some functions called in viewDidLoad and viewDidAppear. I tried using self.tableView.reloadData() at the end of my viewDidLoad but it didn't work and upon reloading the tab, the app would crash.
Here is some of my code (I'm trying to fetch events from a Google Calendar and display it in a TableView). I'm trying to display an array of strings named listOfEvents and it is being populated after the tableView is already loaded.
I also tried adding self.tableView.reloadData() at the end of my fetchEvents() but it also killed my app upon reloading the tab
class CalendarViewController: UITableViewController {
var listOfEvents: [String] = []
private let kKeychainItemName = "Google Calendar API"
private let kClientID = "clientID"
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLAuthScopeCalendarReadonly]
private let service = GTLServiceCalendar()
let output = UITextView()
// When the view loads, create necessary subviews
// and initialize the Google Calendar API service
override func viewDidLoad() {
super.viewDidLoad()
if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
kKeychainItemName,
clientID: kClientID,
clientSecret: nil) {
service.authorizer = auth
}
}
// When the view appears, ensure that the Google Calendar API service is authorized
// and perform API calls
override func viewDidAppear(animated: Bool) {
if let authorizer = service.authorizer,
canAuth = authorizer.canAuthorize where canAuth {
fetchEvents()
} else {
presentViewController(
createAuthController(),
animated: true,
completion: nil
)
}
}
// Construct a query and get a list of upcoming events from the user calendar
func fetchEvents() {
let query = GTLQueryCalendar.queryForEventsListWithCalendarId("primary")
query.maxResults = 10
query.timeMin = GTLDateTime(date: NSDate(), timeZone: NSTimeZone.localTimeZone())
query.singleEvents = true
query.orderBy = kGTLCalendarOrderByStartTime
service.executeQuery(
query,
delegate: self,
didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
)
}
// Display the start dates and event summaries in the UITextView
func displayResultWithTicket(
ticket: GTLServiceTicket,
finishedWithObject response : GTLCalendarEvents,
error : NSError?) {
if let error = error {
showAlert("Error", message: error.localizedDescription)
return
}
var eventString = ""
if let events = response.items() where !events.isEmpty {
for event in events as! [GTLCalendarEvent] {
let start : GTLDateTime! = event.start.dateTime ?? event.start.date
let startString = NSDateFormatter.localizedStringFromDate(
start.date,
dateStyle: .ShortStyle,
timeStyle: .ShortStyle
)
eventString += "\(startString) - \(event.summary)\n"
// An array holding all my upcoming events
listOfEvents.append("\(startString) - \(event.summary)")
print(listOfEvents)
}
} else {
eventString = "No upcoming events found."
}
output.text = eventString
self.tableView.reloadData()
}
// Creates the auth controller for authorizing access to Google Calendar API
private func createAuthController() -> GTMOAuth2ViewControllerTouch {
let scopeString = scopes.joinWithSeparator(" ")
return GTMOAuth2ViewControllerTouch(
scope: scopeString,
clientID: kClientID,
clientSecret: nil,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: "viewController:finishedWithAuth:error:"
)
}
// Handle completion of the authorization process, and update the Google Calendar API
// with the new credentials.
func viewController(vc : UIViewController,
finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
if let error = error {
service.authorizer = nil
showAlert("Authentication Error", message: error.localizedDescription)
return
}
service.authorizer = authResult
dismissViewControllerAnimated(true, completion: nil)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.listOfEvents.count)
return self.listOfEvents.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Events Cell", forIndexPath: indexPath) as UITableViewCell
var event = ""
event = listOfEvents[indexPath.row]
cell.textLabel?.text = event
return cell
}
}
I would appreciate any help and insight :-) Thanks so much!
After output.text = eventString, you should reload the tableview.

How to know if user rejected the Location Services in Swift 2

I'm trying to make location service app and i have the following code so when the user goes to that view controller he will get an alert of getting the current location.
This is the code
override func viewDidLoad() {
super.viewDidLoad()
// 1. status is not determined
if CLLocationManager.authorizationStatus() == .NotDetermined {
locationManager.requestAlwaysAuthorization()
}
// 2. authorization were denied
else if CLLocationManager.authorizationStatus() == .Denied {
SwiftSpinner.hide()
let alert = UIAlertController(title: "Error with Your Location" , message: "Location services were previously denied. Please enable location services for this app in Settings.", preferredStyle: UIAlertControllerStyle.Alert)
let ok = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
}
alert.addAction(ok)
let cancel = UIAlertAction(title: "Back", style: UIAlertActionStyle.Default) {
UIAlertAction in
self.movenav("arxiki")
}
alert.addAction(cancel)
self.presentViewController(alert, animated: true, completion: nil)
}
// 3. we do have authorization
else if CLLocationManager.authorizationStatus() == .AuthorizedAlways {
locationManager.startUpdatingLocation()
}
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.eventsTable.backgroundColor = UIColor.lightGrayColor()
// self.locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
}
My question is the following.
If the user pushes "Do not authorise" How can i get his option so i can send him back to the previous view controller or to alert him with the message that i have?
In order to catch the user selection you need to declare a CLLocationManager object and implement its delegate (CLLocationManagerDelegate) and use the following method for catching it.
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .Denied || status == .NotDetermined{
// User selected Not authorized
}
}
I assume you have already configured the info.plist with the suitable locations parameters.
Hope it helps!

fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)

I am new to code and I am not sure how to get rid of my optional values here. I read somewhere that this could be my problem. any help would be great!
I have been following this tutorial:https://www.youtube.com/watch?v=Qyy8pJd4IWU
#IBAction func dropPhoto(sender: AnyObject) {
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let uniqueReference = firebase?.childByAutoId()
uniqueReference!.setValue(base64EncodedImage)
let key = uniqueReference?.key
_ = mapView.userLocation.location
geofire!.setLocation(mapView.userLocation.location,forKey: key)
}
Whenever you see this error, look for "!"s
There are two lines here that contain a force-unwrap
geofire!.setLocation(mapView.userLocation.location,forKey: key)
and
uniqueReference!.setValue(base64EncodedImage)
you should be able to fix it by simply replacing the ! with a ? , e.g.
geofire?.setLocation(mapView.userLocation.location,forKey: key)
which will cause setLocation to be called only if geoFire is a real value, otherwise if you want to also handle the nil case, the swift way is:
if let geoFire = geoFire {
geoFire.setLocation(mapView.userLocation.location, forKey: key)
}
else{
*do something*
}
You could also add an assert or precondition checking firebase and geofire at the beginning of the function. Here is a third approach that will check those values and stop execution on your debug builds but otherwise simply return on release builds. That will make the later method calls into firebase and geofire safe.
You will still need to identify why one of your references is unexpectedly nil and handle that case. Maybe never call this image picker function in the first place or simply remove the assertFailure statements and let the function return silently without doing anything. Your choice.
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
guard let firebase = firebase else {
assertionFailure("Missing Firebase reference")
return
}
guard let geofire = geofire else {
assertionFailure("Missing Geofire reference")
return
}
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
uniqueReference = firebase.childByAutoId()
uniqueReference.setValue(base64EncodedImage)
geofire.setLocation(mapView.userLocation.location,forKey: uniqueReference.key)
}

Send email In-game using sprite kit in xcode 7 beta3?

I am making an iPad game in sprite kit using swift in xcode 7beta3 and I want the results of the game to be send to the users email after the game is completed. The user should press a button called send and redirect to where they can type in their email-address and send the message. But I have no idea how to make and send an email.
I have been searching all around the internet for an answer to this question, but all are older version answers. I hope you can help.
Thanks in advance
EDIT:
I have been searching some more and i found a solution (here: http://kellyegan.net/sending-files-using-swift/), but I still have a problem. In my GameViewController i have added:
override func viewDidLoad() {
super.viewDidLoad()
let scene = StartGameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func sendEmail() {
//Check to see the device can send email.
if( MFMailComposeViewController.canSendMail() ) {
print("Can send email.")
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
//Set the subject and message of the email
mailComposer.setSubject("Have you heard a swift?")
mailComposer.setMessageBody("This is what they sound like.", isHTML: false)
if let filePath = NSBundle.mainBundle().pathForResource("Math", ofType: "txt") {
print("File path loaded.")
if let fileData = NSData(contentsOfFile: filePath) {
print("File data loaded.")
mailComposer.addAttachmentData(fileData, mimeType: "text/plain", fileName: "Math")
}
}
self.presentViewController(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
The sendMail() is called in one of my gameScenes when you press a button.
The problem is that I get an error when I press that button. It prints out
Can send email.
File path loaded.
File data loaded.
as it should, but then it gives an error:
Could not cast value of type 'UIView' (0x1964ea508) to 'SKView' (0x19624f560).
I think the problem is the self.presentViewController(), but I have no idea how to fix it.

Swift 2 error handling of audio recording code

can someone please assist me? I have some code (which I know runs properly in Xcode 6) that is having difficulty executing in Xcode 7 (due to the changes in error handling for Swift 2). I tried to convert the code using the latest Swift syntax in Xcode 7, but no recommendations were offered. Any suggestions?
//Setup audio session
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord,error: nil)
//Initialize and prepare the recorder
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error:nil)
audioRecorder.meteringEnabled = true;
audioRecorder.prepareToRecord()
audioRecorder.record()
}
#IBAction func stopAudio(sender: UIButton) {
recordingInProgress.hidden = true
//TODO: Stop recording the user's voice
audioRecorder.stop()
var audioSession = AVAudioSession.sharedInstance();
audioSession.setActive(false, error:nil)
}
The Error messages are for the following code:
session.setCategory(AVAudioSessionCategoryPlayAndRecord,error: nil)
Extra argument 'error' in call
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error:nil)
Cannot find an initializer for type 'AVAudioRecorder' that accepts an argument list of type '(URL: NSURL?, settings: nil, error: nil)'
audioSession.setActive(false, error:nil)
****Extra argument 'error' in call****
try this class ive tested this and it works in the latest version of swift and Xcode
import UIKit
import AVFoundation
class RecordViewController: UIViewController {
required init(coder aDecoder: NSCoder) {
var baseString : String = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
var pathComponents = [baseString, NSUUID().UUIDString + ".m4a"]
self.audioURL = NSURL.fileURLWithPathComponents(pathComponents)!
var session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
var recordSettings: [NSObject : AnyObject] = Dictionary()
recordSettings[AVFormatIDKey] = kAudioFormatMPEG4AAC
recordSettings[AVSampleRateKey] = 44100.0
recordSettings[AVNumberOfChannelsKey] = 2
self.audioRecorder = AVAudioRecorder(URL: self.audioURL, settings: recordSettings, error: nil)
self.audioRecorder.meteringEnabled = true
self.audioRecorder.prepareToRecord()
// Super init is below
super.init(coder: aDecoder)
}
you will then have to implement your buttons of course
something like this
#IBAction func recordPressed(sender: AnyObject) {
if self.audioRecorder.recording {
self.audioRecorder.stop()
self.recordButton.setTitle("RECORD", forState: UIControlState.Normal)
self.playButton.enabled = true
} else {
var session = AVAudioSession.sharedInstance()
session.setActive(true, error: nil)
self.audioRecorder.record()
self.recordButton.setTitle("PLAY", forState: UIControlState.Normal)
self.playButton.enabled = false
}
}
#IBAction func playPressed(sender: AnyObject) {
self.audioPlayer = AVAudioPlayer(contentsOfURL: self.audioURL, error: nil)
audioPlayer.play()
}
}

Resources