How to detect the switching to different wifi networks in iOS? - ios

I'm going through the issue in identifying network switching, let's assume I have connected to wifi-1 and then immediately switched to wifi-2.
So, here is my problem that when I'm switching to wifi-2 vice versa I'm not getting updated. To get to know these status I have used Reachability and Network framework. But, still didn't found the solution.
Note: Few time I'm getting updated, when it takes time to switching to the next wifi.
Used the below code of Alamofire reachability
NetworkReachabilityManager.default?.startListening(onUpdatePerforming: { status in
switch status {
case .notReachable:
print("Off")
DispatchQueue.main.async {
self.view.backgroundColor = .red
}
case .unknown:
print("Unknown")
DispatchQueue.main.async {
self.view.backgroundColor = .orange
}
case .reachable(let type):
print("On - \(type)")
DispatchQueue.main.async {
self.view.backgroundColor = .green
}
}
})
Used the below code of Network
let monitor = NWPathMonitor()
monitor.start(queue: DispatchQueue.global(qos: .background))
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
DispatchQueue.main.async {
self.view.backgroundColor = .green
}
}else{
DispatchQueue.main.async {
self.view.backgroundColor = .red
}
}
}

Related

Skip Button doesn't work for google pre roll on iOS

On iOS app with GoogleInteractiveMediaAds integrated "Skip Ad" button doesn't work. Meanwhile manual call adsManager.skip() works perfectly. The button itself reacts to the tuches because it changes bounds and seems highlighted. Unfortunately, I haven't found anything according to handle tap manually, so maybe somebody has already been in this situation and could help with it.
guard
let adInformation = delegate?.latestAdInformation(), let url = adInformation.urlForIMA,
let adContainer = delegate?.videoAdDisplayContainerView()
else { return }
switch adInformation.adsType {
case .interstitials, .none:
self.play(ignoreAds: true)
return
case .prerolls, .all:
fallthrough
#unknown default:
break
}
let adDisplayContainer = IMAAdDisplayContainer(adContainer: adContainer, companionSlots: nil)
let request = IMAAdsRequest(
adTagUrl: url,
adDisplayContainer: adDisplayContainer,
contentPlayhead: contentPlayhead,
userContext: nil)
adsLoader.requestAds(with: request)
func adsLoader(_ loader: IMAAdsLoader!, adsLoadedWith adsLoadedData: IMAAdsLoadedData!) {
// Grab the instance of the IMAAdsManager and set ourselves as the delegate
adsManager = adsLoadedData.adsManager
adsManager?.delegate = self
// Create ads rendering settings and tell the SDK to use the in-app browser.
let adsRenderingSettings = IMAAdsRenderingSettings()
if let vc = delegate?.adWebControllerPreferredOpenViewController() {
adsRenderingSettings.webOpenerPresentingController = vc
}
// Initialize the ads manager.
adsManager?.initialize(with: adsRenderingSettings)
}
func adsLoader(_ loader: IMAAdsLoader!, failedWith adErrorData: IMAAdLoadingErrorData!) {
self.play(ignoreAds: true)
}
func adsManager(_ adsManager: IMAAdsManager!, didReceive event: IMAAdEvent!) {
if event.type == IMAAdEventType.LOADED {
adsManager.start()
}
}
func adsManager(_ adsManager: IMAAdsManager!, didReceive error: IMAAdError!) {
self.play(ignoreAds: true)
}
func adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager!) {
self.pause(ignoreAds: true)
}
func adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager!) {
self.play(ignoreAds: true)
}

SKStoreReviewController.requestReview() freezes app when there's no cellular service, even when running on a background thread

I've included my code below. If there's no wifi and no cellular service, the app freezes. If I turn cellular off altogether, it prints ""Not reachable", which is expected, and the app works fine. But if there's no service and cellular is on, (for instance, in the subway), it says "reachable via cellular" and I can't interact with the app. It's just frozen, even though that code is on the background thread.
let reachability = Reachability()
#IBAction func rateButtonAction(_ sender: Any) {
if #available(iOS 10.3, *) {
DispatchQueue.global(qos: .background).async {
if self.reachability?.connection == .wifi {
print("Reachable via WiFi")
SKStoreReviewController.requestReview()
} else if self.reachability?.connection == .cellular {
print("Reachable via Cellular")
SKStoreReviewController.requestReview()
} else if self.reachability?.connection == .none {
print("Not reachable")
} else {
print("Not reachable")
}
}
} else {
print("Rate didn't work")
}
}

swift ask music permission for initial view controller

I have a split view controller with the top view controller set to a table view controller that is to display a list of playlists for selection. The first time the app is loaded it asks for music access permission. Answering yes does give it permission but the table view displays no playlists. I end up having to kill the app and run it again. Am I asking for music library permission the wrong place? It is in that top view controller's viewWillAppear and store the playlists I'm using (since some are screened out) in a class of playlists.
override func viewWillAppear(_ animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.isCollapsed
super.viewWillAppear(animated)
checkMediaAccessAndSetup()
}
func checkMediaAccessAndSetup() {
let authorizationStatus = MPMediaLibrary.authorizationStatus()
switch authorizationStatus {
case .notDetermined:
// Show the permission prompt.
MPMediaLibrary.requestAuthorization({[weak self] (newAuthorizationStatus: MPMediaLibraryAuthorizationStatus) in
// Try again after the prompt is dismissed.
self?.checkMediaAccessAndSetup()
})
case .denied, .restricted:
// Do not use MPMediaQuery.
return
default:
// Proceed as usual.
break
}
// Do stuff with MPMediaQuery
self.setupPlaylistStore()
tableView.reloadData()
}
The chief problems with your code are
You are completely failing to grapple with the fact that the requestAuthorization completion function is called on a background thread. You need to step out to the main thread to do work on the interface.
You have omitted the all-important .authorized case. When you have work to do that depends upon your authorization status, you must do it now if you are authorized, but after authorization if you are not determined.
Thus, this is the correct scheme for a coherent authorization check (where f() is the thing you always want to do if you can):
let status = MPMediaLibrary.authorizationStatus()
switch status {
case .authorized:
f()
case .notDetermined:
MPMediaLibrary.requestAuthorization() { status in
if status == .authorized {
DispatchQueue.main.async {
f()
}
}
}
// ...
}
If you abstract this code into a utility method, where f can be anything, you can do this everywhere in your app where authorization might be necessary — not merely at startup.
Thanks for the comments, it gave me clues of the multi-threading that was going on and I was able to fix it with a timer call if there were no playlists in the class to keep on checking and reload the table data.
override func viewWillAppear(_ animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.isCollapsed
super.viewWillAppear(animated)
checkMediaAccess()
self.setupPlaylistStore()
tableView.reloadData()
if store.allPlaylists.count < 1 {
playlistTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.playlistTimerCall), userInfo: nil, repeats: true)
}
}
#objc func playlistTimerCall() {
self.setupPlaylistStore()
if store.allPlaylists.count > 1 {
tableView.reloadData()
playlistTimer?.invalidate()
}
}
func checkMediaAccess() {
let authorizationStatus = MPMediaLibrary.authorizationStatus()
switch authorizationStatus {
case .notDetermined:
// Show the permission prompt.
MPMediaLibrary.requestAuthorization({[weak self] (newAuthorizationStatus: MPMediaLibraryAuthorizationStatus) in
// Try again after the prompt is dismissed.
self?.checkMediaAccess()
})
case .denied, .restricted:
// Do not use MPMediaQuery.
return
default:
// Proceed as usual.
break
}
}
func setupPlaylistStore() {
// purge store
store.clearAllPlaylists()
// create a query of media items in playlist
let myPlayListsQuery = MPMediaQuery.playlists()
if myPlayListsQuery.collections != nil {
playlists = myPlayListsQuery.collections!
}
// add playlists to MyPlaylist(s)
if playlists.count > 0 {
for index in 0...playlists.count - 1 {
let playlist = playlists[index]
store.addPlaylist(playlist: playlist as! MPMediaPlaylist)
}
}
var toBeRemoved = [Int]()
let defaults = UserDefaults.standard
if defaults.bool(forKey: "exclude_smart_playlists") {
//smart
for index in 0...(playlists.count - 1) {
let playlist = playlists[index]
let theAttributes = playlist.value(forProperty: MPMediaPlaylistPropertyPlaylistAttributes) as! Int
if theAttributes == 2 {
toBeRemoved.append(index)
}
}
}
if defaults.bool(forKey: "exclude_folders") {
//folders
for index in 0...(playlists.count - 1) {
let playlist = playlists[index]
let isFolder = playlist.value(forProperty: "isFolder")
let stringIsFolder = String("\(String(describing: isFolder))")
if ((stringIsFolder.range(of: "1")) != nil) {
toBeRemoved.append(index)
}
}
}
//sort from the last to the first so i don't reindex
let reverseSortedPlaylists = toBeRemoved.sorted(by: >)
// remove the unwanted playlists
for list in reverseSortedPlaylists {
store.removePlaylist(index: list)
}
}

How to switch camera while video recording? (Ex: Snapchat, Facebook..etc)

I'm trying to switch camera while recording video like 'Snapchat' and 'Facebook' behavior. Switch camera work fine before start record video, but need to handle it separately when recording.
Any help appreciate...
public func switchCamera() {
guard isVideoRecording != true else {
//TODO: Handle switch camera when recording in here
return
}
guard session.isRunning == true else {
return
}
switch currentCamera {
case .front:
currentCamera = .rear
case .rear:
currentCamera = .front
}
session.stopRunning()
sessionQueue.async { [unowned self] in
// remove and re-add inputs and outputs
for input in self.session.inputs {
self.session.removeInput(input as! AVCaptureInput)
}
// add new input
self.addInputs()
self.session.startRunning()
}
}
You can try to write your frames with AVAssetWriter. Here you can find an example.

How can I detect external microphone in iOS?

How can I detect, from within my application, if an external microphone is plugged in the device?
Try this:
let route = AVAudioSession.sharedInstance().currentRoute
for port in route.outputs {
if port.portType == AVAudioSessionPortHeadphones {
// Headphones located
}
}
EDIT: Post OP change in question -
When app is running you need to register for AVAudioSessionRouteChangeNotification to listen to the changes like this:
NSNotificationCenter.defaultCenter().addObserver(self, selector:"audioRouteChangeListener:", name: AVAudioSessionRouteChangeNotification, object: nil)
dynamic private func audioRouteChangeListener(notification:NSNotification) {
let audioRouteChangeReason = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as UInt
switch audioRouteChangeReason {
case AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue:
println("headphone plugged in")
case AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue:
println("headphone pulled out")
default:
break
}
}
swift3:
NotificationCenter.default.addObserver(self, selector: #selector(audioRouteChangeListener(notification:)), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
#objc private func audioRouteChangeListener(notification: Notification) {
let rawReason = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as! UInt
let reason = AVAudioSessionRouteChangeReason(rawValue: rawReason)!
switch reason {
case .newDeviceAvailable:
print("headphone plugged in")
case .oldDeviceUnavailable:
print("headphone pulled out")
default:
break
}
}
With swift 2.0 this code works
func audioRouteChangeListenerCallback (notif: NSNotification){
let userInfo:[NSObject:AnyObject] = notif.userInfo!
let routChangeReason = UInt((userInfo[AVAudioSessionRouteChangeReasonKey]?.integerValue)!)
switch routChangeReason {
case AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue:
print("Connected");
break;
case AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue:
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)
} catch _ {
}
print("Connected");
break;
case AVAudioSessionRouteChangeReason.CategoryChange.rawValue:
break;
default:
break;
}
}
With AVAudioSession you can list the availableInputs
let session = AVAudioSession.sharedInstance()
_ = try? session.setCategory(AVAudioSessionCategoryRecord, withOptions: [])
print(AVAudioSession.sharedInstance().availableInputs)
It return an array of AVAudioSessionPortDescription. And you can have the portType "wired or builtIn" microphone type.
PS : It only work on real device not on simulator.

Resources