swift - 我一遍又一遍地使用相同的代码块。我如何巩固它?

标签 swift struct dry subclassing

作为 Swift 菜鸟,我发现自己在复制和粘贴代码。我知道我应该使用 DRY 方法而不是这样做,但是这段特殊的代码让我感到难过。我尝试创建一个结构来保存它,但该结构抛出了各种错误。我不太了解类以及如何对其进行子类化,所以也许这就是解决方案。我只是不知道该怎么做?或者可能是扩展?

无论如何,这是我在每个新 View Controller 中不断复制和粘贴的代码:

import UIKit
import AVKit

class Step3JobSummaryVC: UIViewController, UITableViewDataSource, UITableViewDelegate {

...

var sourceVCIdentity = "setup"

var initialLaunch = true
let playerVC = AVPlayerViewController()
let video = Video.step3JobsSummary

...


// ------------------------
// Autoplay Video Functions
// ------------------------

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if initialLaunch == true {
        showUnplayedVideo()
        initialLaunch = false
    }

    Video.updatePlaybackTime(playerVC: playerVC, videoURL: video.url, firebaseVideoID: video.firebaseID)
}

func showUnplayedVideo() {

    // 1. get current video data
    Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in

        if !watched {

            // 2. show setup video popup on first load
            guard let videoURL = URL(string: self.video.url) else { print("url error"); return }
            let player = AVPlayer(url: videoURL)

            self.playerVC.player = player

            // 3. fast forward to where user left off (if applicable)
            player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))

            // 4. dismiss the player once the video is over and update Firebase
            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(self.playerDidFinishPlaying),
                                                   name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                                   object: self.playerVC.player?.currentItem)

            self.present(self.playerVC, animated: true) {
                self.playerVC.player?.play()
            }
        }
    }
}

@objc func playerDidFinishPlaying(note: NSNotification) {
    self.playerVC.dismiss(animated: true)
    Video.updateFirebase(firebaseVideoID: video.firebaseID)
}

任何帮助都会很棒。我只是想学习 :-)

编辑#1

这是我对扩展的尝试。我简化并重构了我的代码,但和以前一样,它给了我一个错误。这次错误是“扩展不能包含存储的属性”。那么如何访问 AVPlayerController?!?

extension UIViewController {

let playerVC = AVPlayerViewController()

func showUnplayedVideo(video: Video) {

    // 1. get current video data
    Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in

        if !watched {
            // 2. show setup video popup on first load
            guard let videoURL = URL(string: video.url) else { print("url error"); return }
            let player = AVPlayer(url: videoURL)

            self.playerVC.player = player

            // 3. fast forward to where user left off (if applicable)
            player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))

            // 4. dismiss the player once the video is over and update Firebase
            NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                                   object: playerVC.player?.currentItem,
                                                   queue: .main) { (notification) in

                                                    self.playerDidFinishPlaying(note: notification as NSNotification)

            self.present(self.playerVC, animated: true) {
                self.playerVC.player?.play()
            }
        }
    }
}

    func playerDidFinishPlaying(note: NSNotification, video: Video) {
        self.playerVC.dismiss(animated: true)
        Video.updateFirebase(firebaseVideoID: video.firebaseID)
    }
}

编辑 #2

所以我得到了编译代码,没有任何错误,但现在它没有触发。啊。

extension UIViewController {

func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video) {

    print("does this code even fire?")

    // 1. get current video data
    Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in

        if !watched {
            // 2. show setup video popup on first load
            guard let videoURL = URL(string: video.url) else { print("url error"); return }
            let player = AVPlayer(url: videoURL)

            playerVC.player = player

            // 3. fast forward to where user left off (if applicable)
            player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))

            // 4. dismiss the player once the video is over and update Firebase
            NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                                   object: playerVC.player?.currentItem,
                                                   queue: .main) { (notification) in

                                                    self.playerDidFinishPlaying(playerVC: playerVC, note: notification as NSNotification, video: video)

                                                    self.present(playerVC, animated: true) {
                                                        playerVC.player?.play()
                                                    }
            }
        }
    }
}

func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video) {
    playerVC.dismiss(animated: true)
    Video.updateFirebase(firebaseVideoID: video.firebaseID)
}
}

为什么这行不通?

最佳答案

我将从为您的功能定义协议(protocol)开始,如下所示:

protocol VideoPlayable {
    func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video)
}

然后给它添加一个默认的实现

extension VideoPlayable where Self: UIViewController {

    func showUnplayedVideo(playerVC: AVPlayerViewController, video: Video) {

       print("does this code even fire?")

       // 1. get current video data
       Video.getFirebaseData(firebaseVideoID: video.firebaseID) { (playbackTime, watched) in

            if !watched {
            // 2. show setup video popup on first load
                guard let videoURL = URL(string: video.url) else { print("url error"); return }
                let player = AVPlayer(url: videoURL)

                playerVC.player = player

                // 3. fast forward to where user left off (if applicable)
                player.seek(to: CMTimeMakeWithSeconds(playbackTime, 1))

                // 4. dismiss the player once the video is over and update Firebase
                NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                                   object: playerVC.player?.currentItem,
                                                   queue: .main) { (notification) in

                    self.playerDidFinishPlaying(playerVC: playerVC, note: notification as NSNotification, video: video)

                }

                self.present(playerVC, animated: true) {
                    playerVC.player?.play()
                }

            }
        }
    }

    private func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video) {
        playerVC.dismiss(animated: true)
        Video.updateFirebase(firebaseVideoID: video.firebaseID)
    }
}

多亏了这一点,当您将 VideoPlayable 协议(protocol)添加到 Controller 时,您将拥有可用的自定义功能,而其他不应具有该功能的 Controller 将无法访问此方法。 另外,如果您真的想访问该方法

func playerDidFinishPlaying(playerVC: AVPlayerViewController, note: NSNotification, video: Video)

将其添加到协议(protocol)中并从实现中删除私有(private)语句。

并且您的视频播放器没有显示,因为您将播放器的显示添加到通知 block 中。

此外,请考虑为您的 block 添加适当的 self 处理。现在我认为 self 可能会陷入困境。

只是让你知道声明 其中 self :UIViewController 将实现的访问限制为 UIViewControllers,因此如果将协议(protocol)添加到 UIView 子类,您将无法访问默认实现。然后您需要添加一个新的 :) 这可以防止在您不希望使用它的地方丢失该协议(protocol)。

关于swift - 我一遍又一遍地使用相同的代码块。我如何巩固它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52729507/

相关文章:

ios - 断言日期时测试失败

arrays - 具有相同匿名字段但类型不同的 Go 结构数组

swift - 如何使用struct在swift中显示UIImage

c - 结构变量打印

django - DRY django 模型,创建对象列表

ios - 隐蔽 3 级嵌套 json 到 Alamofire 参数

arrays - 如何将两个 Unicode 字符合并为一个

ios - 在 Xcode 中从 App store 复制 iOS 应用程序更新过程

javascript - 真正新手的 DRY Javascript

c++ - 如何正确删除模板函数中的代码重复