ニフクラ mobile backend(NCMB)では各種言語向けにSDKを提供しています。その中で最も新しいSwift SDKについて、その使い方を紹介します。
今回から複数回に分けて、カメラメモアプリを作ってみます。Swift SDKの使い方はもちろん、NCMBの基本的な利用法を学ぶのに役立ててください。
なお、今回のコードはNCMBMania/camera_memo_swift: Swift SDKを使ったカメラメモアプリのコードですにアップしてあります。
カメラメモアプリの機能について
カメラメモアプリは、写真を撮影して、そのデータをファイルストアにアップロードします。そして別途メモを書き残すことで、写真とメモを紐付けて保存します。
利用する機能について
カメラメモアプリで利用するNCMBの機能は次の通りです。
- 認証機能
- ID/パスワードによるログイン
- ログアウト
- ファイルストア
- ファイルアップロード
- ファイルダウンロード
- データストア
- ファイルストアと紐付けたメモを保存
画面について
カメラメモアプリでは次の画面(View)を用意しています。
- ContentView
タブバーで2つの画面を読み込んでいます。1つはログインとアップロード画面(UploaderView)、もう1つは写真一覧画面(ImageView)です。 - Imagepicker
写真の撮影またはフォトライブラリから写真を選択します。 - UploaderView
タブバーから読み込まれます。ログイン画面やアップロード画面を表示します。 - MemoView
撮影または選択した写真を表示し、メモとともにNCMBへアップロードします。 - LoginView
NCMBへのユーザ登録、ログイン処理を行います。 - ImageView
NCMBへアップロードした写真を一覧表示します。 - ModalView
写真一覧をタップした際にモーダル表示します。 - GridImageView
写真一覧のグリッド表示用ビューです。
ContentView
タブバーで2つの画面を読み込んでいます。
struct ContentView: View {
var body: some View {
TabView {
UploaderView()
.tabItem {
VStack {
Image(systemName: "photo")
Text("Photo")
}
}.tag(1)
ImageView()
.tabItem {
VStack {
Image(systemName: "rectangle.grid.2x2")
Text("Photos")
}
}.tag(2)
}
}
}
Imagepicker
写真の撮影またはフォトライブラリから写真を選択します。コードは【SwiftUI】カメラ機能の実装方法【撮影画像とライブラリー画像の利用】から拝借しています。ありがとうございます。
UploaderView
ログイン画面やアップロード画面を表示します。ログインしていない場合は LoginView
を表示します。
struct UploaderView: View {
@State var imageData : Data = .init(capacity:0)
@State var source:UIImagePickerController.SourceType = .photoLibrary
@State var isImagePicker = false
@State var isLogin: Bool = NCMBUser.currentUser != nil
var body: some View {
NavigationView{
VStack(spacing:0){
ZStack{
Color.white
.edgesIgnoringSafeArea(.all)
.opacity(0.0)
NavigationLink(
destination: Imagepicker(show: $isImagePicker, image: $imageData, sourceType: source),
isActive:$isImagePicker,
label: {
Text("")
})
VStack{
if isLogin {
VStack(spacing:30){
MemoView(imageData: $imageData)
HStack(spacing:60){
Button(action: {
self.source = .photoLibrary
self.isImagePicker.toggle()
}, label: {
Text("Photo library")
})
Button(action: {
self.source = .camera
self.isImagePicker.toggle()
}, label: {
Text("Take Photo")
})
}
Button(action: {
// ログアウト処理(略)
}, label: {
Text("ログアウト")
})
}
} else {
LoginView(isLogin: $isLogin)
}
}
}
.gesture(
TapGesture()
.onEnded { _ in
UIApplication.shared.closeKeyboard()
}
)
}
.navigationBarTitle("Home", displayMode: .inline)
}
.ignoresSafeArea(.all, edges: .top)
.background(Color.primary.opacity(0.06).ignoresSafeArea(.all, edges: .all))
}
}
MemoView
ログインしている場合に使われる写真とメモを表示する画面です。
struct MemoView: View {
@Binding var imageData : Data
@State private var text: String = ""
@State private var uploaded = false
var body: some View {
if imageData.count != 0 {
Image(uiImage: UIImage(data: self.imageData)!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 250)
.cornerRadius(15)
.padding()
TextField("メモ", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(maxWidth: 280)
Button(action: {
// メモアップロード処理(後述)
}, label: {
Text("Upload")
})
.alert(isPresented: $uploaded, content: {
Alert(title: Text("アップロード完了"), message: Text("写真をアップロードしました"), dismissButton: .default(Text("閉じる"))
)
})
}
}
}
LoginView
ユーザ登録およびログイン処理を実行します。
struct LoginView: View {
@State private var userName: String = ""
@State private var password: String = ""
@Binding var isLogin: Bool
var body: some View {
VStack(spacing: 16) {
TextField("ユーザ名", text: $userName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(maxWidth: 280)
SecureField("パスワード", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(maxWidth: 280)
Button(action: {
signUpOrLogin()
}, label: {
Text("新規登録/ログイン")
})
}
}
func signUpOrLogin() {
// ユーザ登録とログイン処理(後述)
}
}
ImageView
ファイルストアから画像を取得します。
struct ImageView: View {
@State var memos: [NCMBObject] = []
var columns: [GridItem] = Array(repeating: .init(.fixed(200)), count: 2)
var body: some View {
ScrollView(.vertical) {
LazyVGrid(columns: columns, alignment: .center, spacing: 200) {
ForEach (memos, id: \.objectId) { memo in
GridImageView(memo: memo)
}
}
.onAppear() {
getAllPhotos()
}
}
}
func getAllPhotos() {
// 写真をファイルストアから取得(後述)
}
}
ModalView
写真をタップした際にモーダル表示します。
struct ModalView: View {
@Binding var isActive: Bool
@State var memo: NCMBObject
@State var imageData: Data
var body: some View {
HStack {
Spacer()
VStack {
Spacer()
GeometryReader { geometry in
Image(uiImage: UIImage(data: imageData)!)
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
}
if let text: String = memo["text"] {
Text(text).padding()
}
Button("閉じる") {
isActive = false
}
Spacer()
}
Spacer()
}
.padding()
.background(Color(.black))
}
}
GridImageView
写真一覧のグリッド表示用です。
struct GridImageView: View {
@State private var fileName: String = ""
@State private var imageData: Data? = .init(capacity:0)
@State var memo: NCMBObject? = nil
@State private var isShowing = false
var body: some View {
GeometryReader { geometry in
if imageData?.count ?? 0 > 0 {
Image(uiImage: UIImage(data: imageData!)!)
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.width)
.clipped()
} else {
Rectangle().fill(Color.clear)
}
}.onAppear() {
loadImage()
}.onTapGesture {
isShowing = true
}.fullScreenCover(isPresented: $isShowing) {
ModalView(isActive: $isShowing, memo: memo!, imageData: imageData!)
}
}
func loadImage() {
// 写真データを読み込みます(後述)
}
}
Swift SDKの導入法
Swift SDKはCocoaPods向けに提供しています。Xcodeで新しいiOSプロジェクトを作ったら、Podfileを用意します。
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'todoapp' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for todoapp
pod 'NCMB', :git => 'https://github.com/NIFCLOUD-mbaas/ncmb_swift.git'
end
そして pod install
でSwift SDKをインストールします。
初期化について
現在、新規でiOSアプリを作成すると、InterfaceがSwiftUI、Life CycleがSwiftUI Appとなっています。この状態で作ると AppDelegate.swift
はなく、 (アプリ名)App.swift
というファイルが作られます。
この場合、まずSwift SDKを読み込みます。
import SwiftUI
import NCMB // 追加
そして var body
の上に @Environment
を追加します。
@Environment(\.scenePhase) private var scenePhase // 追加
var body: some Scene {
最後に WindowGroup
に onChange
を追加します。このonChangeの中で初期化処理を行います。
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { scene in
switch scene {
case .active:
NCMB.initialize(applicationKey: "YOUR_APPLICATION_KEY", clientKey: "YOUR_CLIENT_KEY")
case .background:
print("background")
case .inactive:
print("inactive")
@unknown default:
print("default")
}
}
これで利用可能になります。
まとめ
今回は必要な画面の内容と、NCMBのSwift SDK導入までを紹介しました。次回は認証処理と写真アップロードを解説します。