How to send data from AppDelegate to my subclass of ViewController in swift 4 - uiviewcontroller

I am using Swift 4.1 and I am wondering how to pass the url data that is given here:
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
}
into my subclass of UIViewController I called HotSpotRISViewController. Here is what I attempted:
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let vc = HotSpotRISViewController()
vc.setURL(theURL: url)
let navigationController = UINavigationController(rootViewController: vc)
self.window!.rootViewController = navigationController
}
My HotSpotRISViewController contains this function:
func setURL(theURL: URL) {
self.url = theURL
print(self.url ?? "nil")
}
Inside my HotSpotRISViewController I have a property of type URL ready to be set. The above code correctly passes the info, because my print statement prints out the URL, but I get a black screen instead of my app. What am I missing? And then on the other hand, the following makes the app start up correctly but I have no idea how to pass the url data using this method:
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = mainStoryboard.instantiateInitialViewController()
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
I'm wondering how I can get both my app started up correctly without a black screen and pass the url data. Thank you.

You can add a global variable in your appDelegate file and fetch it with your setURL function :
In your AppDelegate.swift
var myurl: URL!
....
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
self.myurl = url
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = mainStoryboard.instantiateInitialViewController()
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
...
In your HotSpotRISViewController.swift
func setURL(theURL: URL) {
DispatchQueue.main.async {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
self.url = appDelegate.myurl
}
}

Related

getting failing url error after fetching image from url

I want to fetch image from URL into my TableView. I create extension on UIImageView so I can download image:
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
guard let url = URL(string: "https://image.tmdb.org/t/p/original\(link)") else { return }
print(url)
downloaded(from: url, contentMode: mode)
}
}
In second downloaded function I added a URL value to parameter because JSON object contains only half of URL, so I needed to add this prefix to fully open link.
In my TableViewCell file I made configureCell function with imageUrl parameter which is half url from JSON Object.
func configureCell(songName: String, songName2: String, imageUrl: String) {
songNameLabel.text = songName
songNameLabel2.text = songName2
if let url = URL(string: imageUrl) {
artistImageView.downloaded(from: url)
}
}
In cellForRowAt function in ViewController I added this code
let song = movieList[indexPath.row]
cropCell.configureCell(songName: song.title, songName2: song.overview, imageUrl: song.backdropPath)
Function is configuring labels well, but for image I am getting this error
Task .<2> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=/620hnMVLu6RSZW6a5rwO8gqpt0t.jpg, NSErrorFailingURLKey=/620hnMVLu6RSZW6a5rwO8gqpt0t.jpg, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask .<2>"
)
which opens only backdropPath URl without prefix I added.
JSON Object:
"results":[{"adult":false,"backdrop_path":"/620hnMVLu6RSZW6a5rwO8gqpt0t.jpg","genre_ids":[16,35,10751,14],"id":508943,"original_language":"en","original_title":"Luca","overview":"Luca and his best friend Alberto experience an unforgettable summer on the Italian Riviera. But all the fun is threatened by a deeply-held secret: they are sea monsters from another world just below the water’s surface.","popularity":7586.545,"poster_path":"/jTswp6KyDYKtvC52GbHagrZbGvD.jpg","release_date":"2021-06-17","title":"Luca","video":false,"vote_average":8.2,"vote_count":1250}
Where am I getting wrong?
you call downloaded(from url: URL...) method from the configureCell method, not the downloaded(from link: String...)

Temp JSON data - Swift

So I am trying to create a temp JSON depending on whether a listener is tuned into a podcast or a radio station.
Because we already have all the podcast info we don't need to ask a remote JSON for it. But we still need to have the ability that when the MusicPlayer is playing that in the View it shows the cover, artist and title.
So my thinking was create a simple JSON Struct inside the MusicPlayer class and when the media player sends the data to the following
func getArtBoard(artist: String, song: String, cover: String) {
guard let url = URL(string: cover) else { return }
getData(from: url) { [weak self] image in
guard let self = self,
let downloadedImage = image else {
return
}
let artwork = MPMediaItemArtwork.init(boundsSize: downloadedImage.size, requestHandler: { _ -> UIImage in
return downloadedImage
})
self.nowplaying(with: artwork, artist: artist, song: song)
}
}
It would also save it temp to - which is inside the
class MusicPlayer {
struct NowPlaying: Codable, Identifiable {
var id: ObjectIdentifier
var artist : String
var song : String
var cover : String
}
//more code here
}
However I am getting two errors
Type 'MusicPlayer.NowPlaying' does not conform to protocol 'Decodable'
Type 'MusicPlayer.NowPlaying' does not conform to protocol 'Encodable'
Questions:
How do I make it to conform. - But we able to call it via Music.NowPlaying.
Is this the best way to do it, or can I access the MPMediaItemPropertyTitle in View?
The complete code.
import Foundation
import AVFoundation
import MediaPlayer
import AVKit
class MusicPlayer {
static let shared = MusicPlayer()
static var mediatype = ""
struct NowPlaying: Codable, Identifiable {
var id: ObjectIdentifier
var artist : String
var song : String
var cover : String
}
var player: AVPlayer?
let playerViewController = AVPlayerViewController()
func gettype(completion: #escaping (String) -> Void){
completion(MusicPlayer.mediatype)
}
func getNowPlayingView(completion: #escaping (String) -> Void){
completion(MusicPlayer.mediatype)
}
func startBackgroundMusic(url: String, type:String) {
MusicPlayer.mediatype = String(type)
//let urlString = "http://stream.radiomedia.com.au:8003/stream"
let urlString = url
guard let url = URL.init(string: urlString) else { return }
let playerItem = AVPlayerItem.init(url: url)
player = AVPlayer.init(playerItem: playerItem)
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.duckOthers, .defaultToSpeaker, .mixWithOthers, .allowAirPlay])
print("Playback OK")
// let defaults = UserDefaults.standard
// defaults.set("1", forKey: defaultsKeys.musicplayer_connected)
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
// let defaults = UserDefaults.standard
// defaults.set("0", forKey: defaultsKeys.musicplayer_connected)
print(error)
}
#if targetEnvironment(simulator)
self.playerViewController.player = player
self.playerViewController.player?.play()
print("SIMULATOR")
#else
self.setupRemoteTransportControls()
player?.play()
#endif
}
func startBackgroundMusicTwo() {
let urlString = "http://stream.radiomedia.com.au:8003/stream"
//let urlString = url
guard let url = URL.init(string: urlString) else { return }
let playerItem = AVPlayerItem.init(url: url)
player = AVPlayer.init(playerItem: playerItem)
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.duckOthers, .defaultToSpeaker, .mixWithOthers, .allowAirPlay])
print("Playback OK")
// let defaults = UserDefaults.standard
// defaults.set("1", forKey: defaultsKeys.musicplayer_connected)
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
// let defaults = UserDefaults.standard
// defaults.set("0", forKey: defaultsKeys.musicplayer_connected)
print(error)
}
#if targetEnvironment(simulator)
self.playerViewController.player = player
self.playerViewController.player?.play()
print("SIMULATOR")
#else
self.setupRemoteTransportControls()
player?.play()
#endif
}
func setupRemoteTransportControls() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
// Add handler for Play Command
commandCenter.playCommand.addTarget { [unowned self] event in
if self.player?.rate == 0.0 {
self.player?.play()
return .success
}
return .commandFailed
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { [unowned self] event in
if self.player?.rate == 1.0 {
self.player?.pause()
return .success
}
return .commandFailed
}
// self.nowplaying(artist: "Anna", song: "test")
}
func nowplaying(with artwork: MPMediaItemArtwork, artist: String, song: String){
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle:song,
MPMediaItemPropertyArtist:artist,
MPMediaItemPropertyArtwork: artwork,
MPNowPlayingInfoPropertyIsLiveStream: true
]
// self.getArtBoard();
}
func setupNowPlayingInfo(with artwork: MPMediaItemArtwork) {
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
// MPMediaItemPropertyTitle: "Some name",
// MPMediaItemPropertyArtist: "Some name",
MPMediaItemPropertyArtwork: artwork,
//MPMediaItemPropertyPlaybackDuration: CMTimeGetSeconds(currentItem.duration),
//MPNowPlayingInfoPropertyPlaybackRate: 1,
//MPNowPlayingInfoPropertyElapsedPlaybackTime: CMTimeGetSeconds(currentItem.currentTime())
]
}
func getData(from url: URL, completion: #escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let data = data {
completion(UIImage(data:data))
}
})
.resume()
}
func getArtBoard(artist: String, song: String, cover: String) {
guard let url = URL(string: cover) else { return }
getData(from: url) { [weak self] image in
guard let self = self,
let downloadedImage = image else {
return
}
let artwork = MPMediaItemArtwork.init(boundsSize: downloadedImage.size, requestHandler: { _ -> UIImage in
return downloadedImage
})
self.nowplaying(with: artwork, artist: artist, song: song)
}
}
func stopBackgroundMusic() {
guard let player = player else { return }
player.pause()
}
}
UPDATE
Got the above error solved thanks to the comment below.
However am having issues fetching the data.
Type 'MusicPlayer.NowPlayingData.Type' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
The code I used. - https://developer.apple.com/documentation/foundation/jsonencoder
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(NowPlayingData)
print(String(data: data, encoding: .utf8)!)
To write it I placed
func getArtBoard(artist: String, song: String, cover: String) {
MusicPlayer.NowPlayingData(artist: artist, song: song, cover: cover)
}
DATA trying to encode
func getArtBoard(artist: String, song: String, cover: String) {
//MusicPlayer.NowPlayingData(artist: artist, song: song, cover: cover)
let pear = "{'artist':\(artist), 'song': \(song), 'cover':\(cover)}"
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
MusicPlayer.JN = try encoder.encode(pear)

How to modify the video format after the download in swift

I have a problem in modifying the video format after downloading from videoplayback to mp4 and save to camera.
This is my download code, but I downloaded some video with a different format example : "videoplayback". I can't save to camera because I want to change format video to mp4.
func SessionDownload(URLSession : String) {
MBProgressHUD.hideAllHUDs(for: view, animated: true)
let hud = MBProgressHUD.showAdded(to: self.view, animated: true)
// Set the bar determinate mode to show task progress.
progress = 0.0
hud?.mode = MBProgressHUDMode.determinateHorizontalBar
hud?.isUserInteractionEnabled = true;
hud?.labelText = NSLocalizedString("Downloading...", comment: "HUD loading title")
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
// Do something useful in the background and update the HUD periodically.
self.doSomeWorkWithProgress()
DispatchQueue.main.async(execute: {() -> Void in
//hud?.hide(true)
hud?.labelText = NSLocalizedString("Just Wait...", comment: "HUD loading title")
})
})
let videoPath = URLSession
print(videoPath)
let s = videoPath
let url = NSURL(string:s)!
let req = NSMutableURLRequest(url:url as URL)
let config = URLSessionConfiguration.default
let task = self.session.downloadTask(with: req as URLRequest)
self.task = task
task.resume()
}
//MARK:- share video
func doSomeWorkWithProgress() {
// This just increases the progress indicator in a loop.
while progress < 1.0 {
DispatchQueue.main.async(execute: {() -> Void in
print(self.progress)
MBProgressHUD(for: self.view).progress = self.progress
})
usleep(50000)
}
}
//MARK:- URL Session delegat
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("downloaded \(100*totalBytesWritten/totalBytesExpectedToWrite)")
taskTotalBytesWritten = Int(totalBytesWritten)
taskTotalBytesExpectedToWrite = Int(totalBytesExpectedToWrite)
percentageWritten = Float(taskTotalBytesWritten) / Float(taskTotalBytesExpectedToWrite)
print(percentageWritten)
let x = String(format:"%.2f", percentageWritten)
print(x)
self.progress = Float(x)!
print(progress)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
// unused in this example
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("completed: error: \(error)")
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Finished downloading!")
let fileManager = FileManager()
// this can be a class variable
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
print(directoryURL)
let docDirectoryURL = NSURL(fileURLWithPath: "\(directoryURL)")
print(docDirectoryURL)
//Save To Photos
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL:directoryURL)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
let destinationFilename = downloadTask.originalRequest?.url?.lastPathComponent
print(destinationFilename!)
// append that to your base directory
let destinationURL = docDirectoryURL.appendingPathComponent("\(destinationFilename!)")
print(destinationURL!)
/* check if the file exists, if so remove it. */
if let path = destinationURL?.path {
if fileManager.fileExists(atPath: path) {
do {
try fileManager.removeItem(at: destinationURL!)
} catch let error as NSError {
print(error.debugDescription)
}
}
}
do
{
try fileManager.copyItem(at: location, to: destinationURL!)
}
catch {
print("Error while copy file")
}
DispatchQueue.main.async(execute: {() -> Void in
MBProgressHUD.hide(for: self.view, animated: true)
})
// let videoLink = NSURL(fileURLWithPath: filePath)
let objectsToShare = [destinationURL!] //comment!, imageData!, myWebsite!]
let activityVC = UIActivityViewController(activityItems: objectsToShare , applicationActivities: nil)
activityVC.setValue("Video", forKey: "subject")
//New Excluded Activities Code
if #available(iOS 9.0, *) {
activityVC.excludedActivityTypes = [UIActivity.ActivityType.airDrop, UIActivity.ActivityType.addToReadingList, UIActivity.ActivityType.assignToContact, UIActivity.ActivityType.copyToPasteboard, UIActivity.ActivityType.mail, UIActivity.ActivityType.message, UIActivity.ActivityType.openInIBooks, UIActivity.ActivityType.postToTencentWeibo, UIActivity.ActivityType.postToVimeo, UIActivity.ActivityType.postToWeibo, UIActivity.ActivityType.print]
} else {
// Fallback on earlier versions
activityVC.excludedActivityTypes = [UIActivity.ActivityType.airDrop, UIActivity.ActivityType.addToReadingList, UIActivity.ActivityType.assignToContact, UIActivity.ActivityType.copyToPasteboard, UIActivity.ActivityType.mail, UIActivity.ActivityType.message, UIActivity.ActivityType.postToTencentWeibo, UIActivity.ActivityType.postToVimeo, UIActivity.ActivityType.postToWeibo, UIActivity.ActivityType.print ]
}
if let popoverController = activityVC.popoverPresentationController {
popoverController.sourceView = self.BtnDownloadVideo
popoverController.sourceRect = self.BtnDownloadVideo.bounds
}
self.present(activityVC, animated: true, completion: nil)
}
I'm assuming when you download a file from the internet, you are sure you are downloading a video in this circumstance? And what you are really wanting is just to change the format, i.e. PathExtension such as .mp4, .png, jpeg, etc.
Iff (if and only if) this is the case, then you can add a file extension on to the path component.
let destinationURL = docDirectoryURL.appendingPathComponent("\(destinationFilename!)").appendingPathExtension("mp4")
Now, when you check your saved files, it will include the ".mp4"
Again, I'm assuming you are 110% confident you are downloading a ".mp4" from the interwebs.

swift: how to pass JSON to secondViewController

I want to pass the JSON data to MainMenuPageViewController.
MainMenuPageViewController UILabel(UsernameLabel)
Thank you
Picture1:
let jsonUserId: String = json["return"] as! String
if (jsonUserId != "0") {
print("username and password correct")
dispatch_async(dispatch_get_main_queue(), {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController")
self.showViewController(vc, sender: self)
//pass the jsonUserId to MainMenuPageViewController
//MainMenuPageViewController has UILabel(UserIdLabel)
})
}
If I input wrong username and password, system will run to line 57 but the alert will error, when I input long String(a,A,#,etc.). However, if I input shot String(shing, herry,123,etc.) app can display the alert message.
Also, if input the space bar and (!##$%^&*()_+). it will error.
Can you help me to fix this error? Thank you.
Just declare variable in MainMenuPageViewController like
class MainMenuPageViewController: UIViewController {
var UserIdLabel = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
and for pass data
let jsonUserId: String = json["return"] as! String
if (jsonUserId != "0") {
print("username and password correct")
dispatch_async(dispatch_get_main_queue(), {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController") as? MainMenuPageViewController {
vc.UserIdLabel = jsonUserId
self.showViewController(vc, sender: self)
}
})
}
You should create a property in the MainMenuPageViewController to store the user id and then in viewDidLoad set the UsernameLabel text to the stored property.
var userID:String! //define this in the MainMenuPageViewController
Then after you create the vc set the userID property to the jsonUserID
if let vc = storyboard.instantiateViewControllerWithIdentifier("MainMenuPageViewController") as? MainMenuPageViewController {
vc.userID = jsonUserId
self.showViewController(vc, sender: self)
} else {
printf("The VC is not of the right type")
}
override func viewDidLoad() {
super.viewDidLoad()
UsernameLabel.text = userID
}

Validating Information Before Moving to Next View in Swift

I have an application where I need to validate some information(zip code) from a database before I allow my iOS application to proceed to the next view. I used the zip code project to import a DB Table will all valid US Zip codes, and I want to have the zip code the inputed by the user validated before I allow them to proceed. If the zip code isn't valid, I hold them up at the current view and display an alert. I have a class to validate the zip code, but the zip code isn't being validated until after the next view is loaded. I've been leaning towards using a completion handler, but I'm not exactly sure if that's my best/only option. Thanks in advance.
EDIT:
The following is the whole class for retrieve the data
protocol ZipCodeLocationProtocol: class {
func zipCodeLocationDownloaded(zipLocation: Location)
}
class RetrieveZipCodeLocation: NSObject, NSURLSessionDataDelegate {
// MARK: Properties
weak var delegate: ZipCodeLocationProtocol!
var data: NSMutableData = NSMutableData()
let urlPath: String = "xxxx"
func downloadZipCodeLocation(zipcode: Int) {
let path = self.urlPath + "?zipcode=\(zipcode)"
let url: NSURL = NSURL(string: path)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
var location = Location(title: "TITLE", coordinate: CLLocationCoordinate2D(latitude: 0, longitude: 0))
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:[]) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
for(var i = 0; i < jsonResult.count; i++) {
jsonElement = jsonResult[i] as! NSDictionary
let point = CLLocationCoordinate2D(latitude: (jsonElement["LATITUDE"] as! NSString).doubleValue, longitude: (jsonElement["LONGITUDE"] as! NSString).doubleValue)
// Get Information
location = Location(title: "TITLE", coordinate: point)
self.delegate.zipCodeLocationDownloaded(location)
}
}
I'm going to assume that a button triggers the segue to the next view. I'm also going to assume that the button is hooked up to a function for target-action. I'm also going to assume that you have the code to get the zip codes, otherwise you'll have to ask a separate question for that.
Assumptions aside, you need to present a UIAlertController instead of going to the next view controller when tapping the button. In order to do that:
func buttonAction() {
if verifyZipCode() {
let alert = UIAlertController(title: "Hold Up", message: "That zip code is invalid.", preferredStyle: .Alert)
let fixIt = UIAlertAction(title: "Fix It!", style: .Default, handler: nil) // handler could also contain code to make text field red or something interesting
alert.addAction(fixIt)
presentViewController(alert, animated: true, completion: nil)
} else {
// existing segue code
}
}
func verifyZipCode() -> Bool {
// Take text field text and verify zip code
}