LoginSignup
11
5

More than 3 years have passed since last update.

【SwiftUI】物理的に平成を振り返るアプリを開発した

Last updated at Posted at 2019-06-14

はじめに

今回はサイバーエージェント主催の平成最後のハッカソンで開発した「平成振り返りデバイス」のiOSアプリ版を最近発表されたSwiftUIで開発したぜ的な記事になります。22909_ext_03_0.png

平成振り返りデバイスとは

以下の写真にあるデバイスを体に身に付けて物理的に振り返るとWebサイトに平成のニュースやtwitterのバズったツイートが届くサービスになっています。デモ動画はこちらにあります。IMG_20190430_132455.jpg

平成振り返りアプリ

平成振り返りデバイスは手元にデバイスが無いと遊べないため、同じようなアプリを作ってみんなに遊んでもらいたい!と思い開発しました。以下のようにiPhoneのジャイロセンサで振り向きを検知したら平成のニュースやツイートが届きます。
ダウンロード.gif

開発環境

  • iOS13
  • Xcode11
  • SwiftUI

View

簡単にソースコードの紹介をします。

CardView

ニュースを表示させるカードのソースコードです。これをList表示します。

CardView.swift
import SwiftUI

struct CardView : View {
    var newsModel: NewsModel!
    var body: some View {
        VStack {
            HStack {
                Spacer()
                Image(uiImage: self.getImageByUrl(url: newsModel.img))
                    .resizable()
                    .frame(width: 300, height: 200)
                    .aspectRatio(contentMode: .fit)
                Spacer()
            }
                Text(newsModel!.title)
                    .font(.title)
            Text("date: \(newsModel.date)")
                    .font(.subheadline)
                    .color(.gray)
            Text("ジャンル: \(newsModel.genre)")
                    .font(.subheadline)
                    .color(.gray)
                PresentationButton(
                    Text("READ MORE")
                        .color(.blue)
                    , destination: ReadMoreView(url: newsModel.url))
                }
                .padding(.top)

    }

    func getImageByUrl(url: String) -> UIImage{
        let url = URL(string: url)
        do {
            let data = try Data(contentsOf: url!)
            return UIImage(data: data)!
        } catch let err {
            print("Error : \(err.localizedDescription)")
        }
        return UIImage()
    }

}

ReadMoreView

ニュースのリンクを表示させるWebViewです。SwiftUIのチュートリアルでMapViewを作っていた感じと同じです。

ReadMoreView.swift
import SwiftUI
import WebKit

struct ReadMoreView : UIViewRepresentable {
    var url: String!

    func makeUIView(context: Context) -> WKWebView {
        WKWebView(frame: .zero)
    }

    func updateUIView(_ view: WKWebView, context: Context) {
        let urlNS = NSURL(string: url)
        let req = NSURLRequest(url:urlNS! as URL)
        view.load(req as URLRequest)
    }

}

ViewModel

ViewModelでは振り向きの検知や、ニュースを取得するAPIを叩いたりしてListに表示するデータを保持します。ジャイロセンサで角速度[rad/s]を取得しそれを積分して90度を超えた時にニュースを取得するようにしています。

LookBackViewModel.swift
import SwiftUI
import Combine
import CoreMotion
import Alamofire

final class LookBackViewModel: BindableObject  {
    let didChange = PassthroughSubject<LookBackViewModel, Never>()
    let motionManager = CMMotionManager()
    let url = [
        "twitter": "https://script.google.com/macros/s/AKfycbyhR8HyQKcf2b9wRUmsCm-6D_EK1zFlJzIpPIhrBuRd49FVFtpT/exec",
        "heisei": "https://script.google.com/macros/s/AKfycbxc-TKyZ8Lp-9Ed05et_wIGw55RLGBGwhNSY2lb2z9iQdy1wLs/exec"]
    var gyro_x: Double! = 0
    var gyro_y: Double! = 0
    var gyro_z: Double! = 0
    var degree:Double! = 0

    var imageName = "heisei" {
        didSet {
            didChange.send(self)
        }
    }

    var newsList: [NewsModel] = [] {
        didSet {
            didChange.send(self)
        }
    }

    init() {
        self.motionCapture()
    }

    func request(url: String) {
        Alamofire.request(url,
            method: .get,
            parameters: nil,
            encoding: JSONEncoding.default,
            headers: nil)
            .response { response in
                guard let data = response.data else { return }
                let newsModel: NewsModel = try! JSONDecoder().decode(NewsModel.self, from: data)
                self.newsList.insert(newsModel, at: 0)
        }
    }

    func motionCapture() {
        motionManager.deviceMotionUpdateInterval = 0.1
        motionManager.startDeviceMotionUpdates( to: OperationQueue.current!, withHandler:{
            deviceManager, error in
            let gyro: CMRotationRate = deviceManager!.rotationRate
            self.gyro_x = gyro.x
            self.gyro_y = gyro.y
            self.gyro_z = gyro.z
            self.loop(angular: gyro.y)
        })
    }

    func loop(angular: Double){
        self.degree += angular * 0.1 * 57.2
        if self.degree >= 90{
            self.imageName = "left"
            self.request(url: self.url["twitter"]!)
            self.degree = 0
        } else if -self.degree >= 90{
            self.imageName = "right"
            self.request(url: self.url["heisei"]!)
            self.degree = 0
        }
    }



}

Model

SwiftのCodableを使ってAPIで取得したニュースのJSONを以下で定義した型に変換します。

NewsModel.swift
import SwiftUI
import Foundation

struct NewsModel: Hashable, Codable {
    let title: String
    let url: String
    let date: String
    let img: String
    let genre: String
}

おわりに

まだチュートリアルで勉強した程度のUI実装なので汚いですがSwiftUIを使って1つのアプリが開発できた事はすごく自信になりました。これからはよりUIのデザインを洗練しアプリの動作もサクサク動くようにしていきたいです。今回の実装はGitHubにアップしています。アドバイスなどありましたらコメントお願いします!!!

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5