Fetch video from server through API using AVPlayer in Swift 3 - swift3

I am trying to fetch video from server in AVPlayer.The data is in JSON format. I have given down data of JSON format too. The video is in .mp4 format.
But the video doesn't play. But before I tried to play the video from local storage of .mp4, it played successfully in AVPlayer. What is problem with my code? I am using Swift 3.
My code is :
//ViewController
import UIKit
import AVFoundation
import AVKit
import Alamofire
class ViewController: UIViewController {
var playerController = AVPlayerViewController()
var player:AVPlayer?
var playerController1 = AVPlayerViewController()
var player1:AVPlayer?
var dictDataImage:NSArray = NSArray()
var dictDataVideo: NSArray = NSArray()
var appDictionary:NSDictionary!
#IBOutlet var videoPreviewLayer2: YTPlayerView!
override func viewDidLoad() {
super.viewDidLoad()
self.videos()
videoPreviewLayer2.delegate = self
}
func videos(){
let collectionviewone: String = "http://rillmark.academy/api/rillmark-academy/home-page-video"
Alamofire.request(collectionviewone, method: .get, parameters: nil, encoding: JSONEncoding.default)
.responseJSON { response in
debugPrint(response.result)
if let JSON = response.result.value{
self.appDictionary = (JSON as AnyObject) as! NSDictionary
print("self.app =",self.appDictionary)
let url1: URL = URL(string: (self.appDictionary.value(forKey: "home_video")) as! String)!
let url4 = url1.absoluteString
print("url11=", url1)
print("url22=", url4)
let url3 = NSURL(fileURLWithPath: url4)
let item = AVPlayerItem(url: url3 as URL)
print("item=", item)
self.player = AVPlayer(playerItem: item)
self.playerController = AVPlayerViewController()
self.playerController.player = self.player
self.playerController.view.frame = self.videoPreviewLayer.frame
self.videoPreviewLayer.addSubview(self.playerController.view)
self.playerController.showsPlaybackControls = false
self.playerController.player?.play()
}
}
}
}
//JSON
{
"status": "Success",
"home_video": "http://video/homepage/e27U0B_1502445369-homepage-2.mp4",
}

Related

SwiftUI - Publishing changes from background threads is not allowed

I am trying to create an app that creates random pictures when a button is clicked. The app is working fine but I see this message which I have never seen before."Publish changes from background threads is not allowed; make sure to publish values from the main thread.".
I am new to SwiftUI, help is appreciated.
import Foundation
import SwiftUI
class ImageviewModel{
var image: UIImage? = nil
//let url = URL(string: "https://source.unsplash.com/random/600x600")!
let url = URL(string: "https://picsum.photos/600/600")!
func responseHandler(data: Data?, response: URLResponse?) ->
UIImage?{
guard let data = data,
let image = UIImage(data: data),
let response = response else {return nil}
return image
}
func loadImageWithAsync() async throws -> UIImage?{
do{
let (data, response) = try await URLSession.shared.data(from: url,delegate: nil)
return responseHandler(data: data, response: response)
} catch{
throw error
}
}
}
class ViewModel: ObservableObject{
#Published var image: UIImage? = nil
var loader = ImageviewModel()
func fetchImage() async {
let image = try? await loader.loadImageWithAsync()
self.image = image
}
}
You need to add the MainActor wrapper to the class to guarantee that updates are done on Main
#MainActor
class ViewModel: ObservableObject{

Implementing Login Navigation in SwiftUI

Here is my requirement: I want to display a login screen, perform authentication via a network request, and if successful, display the content page.
I have implemented the network request part as per the suggestion by jpdnx here.
I have a UserAuth class and the corresponding code to display the Login View if not logged in, and the Content View if logged in, as per M Reza here.
However, I am not able to combine the two - when the network request finishes in LoginView, I am not able to get the flow back to the LoginControllerView to navigate to ContentView. Any help will be appreciate here.
Here is my code:
Network.swift
class ViewModel<T: Codable> : ObservableObject {
//#Published var calendar : IDCCalendar?
#Published var modelData : T?
func getData(url: URL, encoded: String, completion: (#escaping ()->()) ) {
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print(encoded)
let encoder = JSONEncoder()
if let data = try? encoder.encode(encoded) {
request.httpBody = data
}
print("Request: \(request)")
URLSession.shared.dataTask(with: request) { data1, response, error in
if let error = error {
print("Request error: ", error)
return
}
guard let data1 = data1 else { return }
DispatchQueue.main.async {
let decoder = JSONDecoder()
print(String(decoding: data1, as: UTF8.self))
if let value = try? decoder.decode(T.self, from: data1)
{
self.modelData = value
completion()
print("Success!")
}
else {
print("Does not decode correctly")
}
}
}.resume()
}
LoginControllerView.swift
var body: some View {
Group {
if !userAuth.isLoggedin {
LoginView(userAuth: userAuth)
} else {
ContentView()
}
}
LoginView.swift
struct LoginView: View {
#EnvironmentObject var userAuth: UserAuth
#StateObject var viewModel = ViewModel<Login>()
var body: some View {
// Login Screen UI here
Button("Login") {
// Commenting out network request code out for now
/*
let url = URL(<URL goes here>)!
let encoded = <JSON String goes here>
let completion = {
if viewModel.modelData?.Status == 0 {
userAuth.isLoggedin = true
let login = viewModel.modelData!
let encoder = JSONEncoder()
if let data = try? encoder.encode(login) {
UserDefaults.standard.set(data, forKey: "LoginData")
}
}
viewModel.getData(url: url, encoded: encoded, completion: completion)
*/
// For test purposes, setting it to true, I want this to percolate back to
// LoginControllerView
userAuth.isLoggedin = true
}
UserAuth.swift
import Combine
class UserAuth: ObservableObject {
let didChange = PassthroughSubject<UserAuth,Never>()
// required to conform to protocol 'ObservableObject'
let willChange = PassthroughSubject<UserAuth,Never>()
func login() {
// login request... on success:
self.isLoggedin = true
}
var isLoggedin = false {
didSet {
didChange.send(self)
}
}
}

How to draw a Route between CurrentLocation to SearchedLocation in MkMapView in Swift

I need current location as a source and searched location as a destination, but I got the current location but here I am unable to bring coordinates(latitude and longitude) from searched location to destination.
here my destination shows nil why?
Below is the code please help me.
import UIKit
import MapKit
import CoreLocation
class MapSampViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UISearchBarDelegate {
//Privacy - Location When In Use Usage Description, Privacy - Location Always Usage Description-------these two add in info.plist
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var mapView: MKMapView!
var source: CLLocationCoordinate2D!
var destination: CLLocationCoordinate2D!
var myaddress:String!
var mycity:String!
var mystate:String!
var mycountry:String!
var mytitle:String!
var mylongitude:String!
var mylatitude:String!
var locationtoSearch:String!
let locationManager = CLLocationManager()
var currentlocationPlacemark: CLPlacemark!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
mapView.delegate = self
mapView.showsScale = true
mapView.showsPointsOfInterest = true
mapView.showsUserLocation = true
if CLLocationManager.locationServicesEnabled()
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
// self.showDirection()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
locationtoSearch = self.searchBar.text
var geocoder:CLGeocoder = CLGeocoder()
geocoder.geocodeAddressString(locationtoSearch!, completionHandler: {(placemarks, error) -> Void in
if((error) != nil)
{
print("Error", error)
}
else if let placemark = placemarks?[0] as? CLPlacemark {
var coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
var pointAnnotation:MKPointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = coordinates
print(coordinates)
// pointAnnotation.title = "\(String(describing: placemark.name)),\(String(describing: placemark.locality)), \(String(describing: placemark.administrativeArea)), \(String(describing: placemark.country))"
self.myaddress = placemark.name
self.mycity = placemark.locality
self.mystate = placemark.administrativeArea
self.mycountry = placemark.country
pointAnnotation.title = "\(self.myaddress),\(self.mycity),\(self.mystate),\(self.mycountry)"
self.mylongitude = String(stringInterpolationSegment: placemark.location?.coordinate.longitude)
self.mylatitude = String(stringInterpolationSegment: placemark.location?.coordinate.latitude)
self.mapView?.addAnnotation(pointAnnotation)
self.mapView?.centerCoordinate = coordinates
print("coordinates \(coordinates)")
print("The latitude \(self.mylatitude)")
print("The longitude \(self.mylongitude)")
self.mapView?.selectAnnotation(pointAnnotation, animated: true)
}
})
self.showDirection()//i called here or in view viewDidLoad
let annotationsToRemove = mapView.annotations.filter { $0 !== self.mapView.userLocation
}
mapView.removeAnnotations( annotationsToRemove )
}
func showDirection()
{
source = locationManager.location?.coordinate//17.6881° N, 83.2131° E
// let destination = CLLocationCoordinate2DMake(24.9511, 121.2358 )//If i give like this its working
destination = CLLocationCoordinate2DMake(Double(mylongitude)!, Double(mylongitude)!)//fatal error: unexpectedly found nil while unwrapping an Optional value
let sourcePlacemark = MKPlacemark(coordinate: source!)
let destinationPlacemark = MKPlacemark(coordinate: destination)
let sourceItem = MKMapItem(placemark: sourcePlacemark)
let destinationItem = MKMapItem(placemark: destinationPlacemark)
let directionReq = MKDirectionsRequest()
directionReq.source = sourceItem
directionReq.destination = destinationItem
directionReq.transportType = .automobile
let directions = MKDirections(request: directionReq)
directions.calculate(completionHandler: {(response, error) in
if error != nil {
print("Error getting directions")
}
else {
let route = response?.routes[0]
self.mapView.add((route?.polyline)!, level:.aboveRoads)
let rekt = route?.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rekt!), animated: true)
}
})
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let rendrer = MKPolylineRenderer(overlay: overlay)
rendrer.strokeColor = UIColor.blue
rendrer.lineWidth = 3
return rendrer
}
}
here i called showDirection() func in searchBarSearchButtonClicked but it is getting called before coming here why?
Direction requests are executed asynchronously. This means that the rest of your app doesn't wait for the direction to be fetched.
Your showDirection function is both fetching the direction and adding it to the mapView. It would be best to separate these functionalities. You can fetch the direction, update a route variable and have an observer on it which will add the route to the map once it has been fetched.
#IBOutlet weak var mapView: MKMapView!
var route: MKRoute? {
didSet {
mapView.add((route?.polyline)!, level:.aboveRoads) }
}

Swift 3 - Download JPEG image and save to file - macOS

I have a downloader class that downloads a file based on a given URL which then calls a completion passing it the contents of the file as NSData.
For the project that I'm using this in, the URL will be a JPEG image. The downloader works perfectly; I can use the result into NSImage and show it in a Image View Controller.
I would like to be able to save that NSData object to file.
After quite some time researching the internet on Google, StackOverflow, etc. and trying many suggestions, I cannot get the file to save.
Here is a playground of the Downloader class and my attempt to save the file:
//: Playground - noun: a place where people can play
import Cocoa
class NetworkService
{
lazy var configuration: URLSessionConfiguration = URLSessionConfiguration.default
lazy var session: URLSession = URLSession(configuration: self.configuration)
let url: NSURL
init(url: NSURL)
{
self.url = url
}
func downloadImage(completion: #escaping ((NSData) -> Void))
{
let request = NSURLRequest(url: self.url as URL)
let dataTask = session.dataTask(with: request as URLRequest) { (data, response, error) in
if error == nil {
if let httpResponse = response as? HTTPURLResponse {
switch (httpResponse.statusCode) {
case 200:
if let data = data {
completion(data as NSData)
}
default:
print(httpResponse.statusCode)
}
}
} else {
print("Error download data: \(error?.localizedDescription)")
}
}
dataTask.resume()
}
}
let IMAGE_URL = NSURL(string: "https://www.bing.com/az/hprichbg/rb/RossFountain_EN-AU11490955168_1920x1080.jpg")
let networkService = NetworkService(url: IMAGE_URL!)
networkService.downloadImage(completion: { (data) in
data.write(to: URL(string: "file://~/Pictures/image.jpg")!, atomically: false)
})
The playground console show nothing at all. Can anyone spot why its not working?
NOTE: The target is macOS, not iOS. Also, I'm a swift noob...
I did try this:
networkService.downloadImage(completion: { (imageData) in
let imageAsNSImage = NSImage(data: imageData as Data)
if let bits = imageAsNSImage?.representations.first as? NSBitmapImageRep {
let outputData = bits.representation(using: .JPEG, properties: [:])
do {
try outputData?.write(to: URL(string: "file://~/Pictures/myImage.jpg")!)
} catch {
print("ERROR!")
}
}
})
It could be a permission issue. You may try:
let picturesDirectory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)[0]
let imageUrl = picturesDirectory.appendingPathComponent("image.jpg", isDirectory: false)
try? data.write(to: imageUrl)
It does work for me:

Kingfisher and swift 3 - cannot set image with url

Checking the documentation and the migration guide, I should be able to set a new image using this code:
imageView.kf.setImage(with:url ...)
but actually I cannot find this method in the library, I only see:
imageView.kf.setImage(with:Resource... )
I don't know exactly how this resource shoud work though since I cannot find anything in the documentation.
Resource is a protocol. URL has been extended to conform to this protocol. So you can do:
let url = URL(string: ...)!
imageView.kf.setImage(with: url)
If you want some control over what Kingfisher uses for the key in its cache, you can use ImageResource:
let identifier = "..."
let url = URL(string: "http://example.com/images/identifier=\(identifier)")!
let resource = ImageResource(downloadURL: url, cacheKey: identifier)
imageView.kf.setImage(with: resource)
For Swift 4.2
import Kingfisher
extension UIImageView {
func setImage(with urlString: String){
guard let url = URL.init(string: urlString) else {
return
}
let resource = ImageResource(downloadURL: url, cacheKey: urlString)
var kf = self.kf
kf.indicatorType = .activity
self.kf.setImage(with: resource)
}
}
How to use
self.imgVw.setImage(with: your image url)
I fixed that issue using this:
PhotoHelper.shared.imagePickedBlock = { [weak self] (image, url) in
self?.imageView.kf.setImage(with: url, placeholder: image, options: nil, progressBlock: nil, completionHandler: { imageResult, error, type, cache in
self?.imageView.image = image
})
}
PhotoHelper is wrapper on native Image Picker:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
currentVC.dismiss(animated: true, completion: {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let url = info[UIImagePickerControllerReferenceURL] as! URL
self.imagePickedBlock?(image, url)
}
})
}