Merge: iOS: implement data_store and missing life cycle callbacks
authorJean Privat <jean@pryen.org>
Fri, 12 Feb 2016 20:47:52 +0000 (15:47 -0500)
committerJean Privat <jean@pryen.org>
Fri, 12 Feb 2016 20:47:52 +0000 (15:47 -0500)
Implement the main missing features of _app.nit_ on iOS: `data_store` and life-cycle hooks (like `on_save_state`). These changes can be seen on the calculator app, as it preserves its context using the `data_store`.

`data_store` is implemented with `NSUserDefaults` to store objects sertialized to Json. It is very similar to Android's implementation using shared preferences. This may be a bit limited as it is not meant to hold large strings, and some data objects (like game saves) should instead be saved to a file.

This PR also implements all life-cycle callbacks in iOS, until now only `on_create` was implemented. We may have to update _app.nit_ life-cycle to fit better with the life-cycle of iOS, the states between a running app and a fully stopped app are different between iOS and Android. I'm thinking of removing the two callbacks on_start/on_stop and keep only the more general callbacks on_create/on_destroy and on_resume/on_pause, and the services on_restore_state/on_save_state.

Pull-Request: #1947
Reviewed-by: Jean Privat <jean@pryen.org>

lib/app/data_store.nit
lib/binary/serialization.nit
lib/cocoa/foundation.nit
lib/ios/app.nit
lib/ios/data_store.nit [new file with mode: 0644]
lib/ios/ui/ui.nit
lib/json/serialization.nit
lib/serialization/serialization.nit

index 5c59036..4db4b22 100644 (file)
@@ -26,6 +26,7 @@ import serialization
 # TODO: move on the platform once qualified names are understand in the condition
 import linux::data_store is conditional(linux)
 import android::data_store is conditional(android)
+import ios::data_store is conditional(ios)
 
 redef class App
        # Services to store and load data
index e069304..a530ebf 100644 (file)
@@ -336,6 +336,12 @@ redef class Text
 
                return true
        end
+
+       redef fun serialize_to_binary(v)
+       do
+               v.stream.write_byte kind_string
+               v.stream.write_block to_s
+       end
 end
 
 # ---
@@ -397,14 +403,6 @@ redef class Char
        end
 end
 
-redef class String
-       redef fun serialize_to_binary(v)
-       do
-               v.stream.write_byte kind_string
-               v.stream.write_block self
-       end
-end
-
 redef class NativeString
        redef fun serialize_to_binary(v)
        do
index bbb27ab..2996315 100644 (file)
@@ -149,3 +149,24 @@ extern class NSIndexPath in "ObjC" `{ NSIndexPath * `}
                return [self indexAtPosition: position];
        `}
 end
+
+# Interface to the defaults system for an app to customize its behavior to match a user's preferences
+extern class NSUserDefaults in "ObjC" `{ NSUserDefaults * `}
+       super NSObject
+
+       # Wraps: `[NSUserDefaults standardUserDefaults]`
+       new standard_user_defaults in "ObjC" `{
+               return [NSUserDefaults standardUserDefaults];
+       `}
+
+       # Wraps: `[NSIndexPath stringForKey:]`
+       fun string_for_key(key: NSString): NSString in "ObjC" `{
+               return [self stringForKey: key];
+       `}
+
+       # Wraps: `[NSIndexPath setObject: forKey:]`
+       fun set_object(value: NSObject, default_name: NSString)
+       in "ObjC" `{
+               [self setObject:value forKey:default_name];
+       `}
+end
index 1dc461d..b0ffb30 100644 (file)
@@ -139,7 +139,12 @@ redef class App
        # The application just launched but is not yet displayed to the user
        #
        # Redef this method to customize the behavior.
-       fun did_finish_launching_with_options: Bool do return true
+       fun did_finish_launching_with_options: Bool
+       do
+               on_create
+               on_restore_state
+               return true
+       end
 
        # The application is about to move from active to inactive state
        #
@@ -151,7 +156,7 @@ redef class App
        # Redef this method to pause ongoing tasks, disable timers, and
        # throttle down OpenGL ES frame rates. Games should use this
        # method to pause.
-       fun will_resign_active do end
+       fun will_resign_active do on_pause
 
        # The application just left foreground it can be suspended at any time
        #
@@ -161,27 +166,38 @@ redef class App
        #
        # If your application supports background execution, this method
        # is called instead of `will_terminate` when the user quits.
-       fun did_enter_background do end
+       fun did_enter_background
+       do
+               on_save_state
+               on_stop
+       end
 
        # The application will enter the foreground
        #
        # Called as part of the transition from the background to the
        # inactive state.
        #
-       # Redef to und changes made on entering the background.
-       fun will_enter_foreground do end
+       # Redef to undo changes made on entering the background.
+       fun will_enter_foreground do on_start
 
        # The application just became active
        #
        # Redef to restart any tasks that were paused (or not yet started) while
        # the application was inactive. If the application was previously
        # in the background, optionally refresh the user interface.
-       fun did_become_active do end
+       fun did_become_active do on_resume
 
-       # The application is about to terminate (not suspended)
+       # The application is about to terminate (from a state other than suspended)
        #
        # Redef to save data if appropriate.
-       fun will_terminate do end
+       fun will_terminate
+       do
+               # Usually a forced termination by the system
+               on_save_state
+               on_pause
+               on_stop
+               on_destroy
+       end
 end
 
 app.register_args(program_name.to_cstring, args.length, args)
diff --git a/lib/ios/data_store.nit b/lib/ios/data_store.nit
new file mode 100644 (file)
index 0000000..6d1579d
--- /dev/null
@@ -0,0 +1,60 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Implements `app::data_store` using `NSUserDefaults`
+module data_store
+
+import app::data_store
+import cocoa::foundation
+private import json::serialization
+
+redef class App
+       redef var data_store = new UserDefaultView
+end
+
+private class UserDefaultView
+       super DataStore
+
+       # The `NSUserDefaults` used to implement `DataStore`
+       var user_defaults = new NSUserDefaults.standard_user_defaults is lazy
+
+       redef fun [](key)
+       do
+               var nsstr = user_defaults.string_for_key(key.to_nsstring)
+
+               if nsstr.address_is_null then return null
+
+               # TODO report errors
+               var deserializer = new JsonDeserializer(nsstr.to_s)
+               return deserializer.deserialize
+       end
+
+       redef fun []=(key, value)
+       do
+               var nsobject: NSString
+
+               if value == null then
+                       nsobject = new NSString.nil
+               else
+                       var serialized_string = new StringWriter
+                       var serializer = new JsonSerializer(serialized_string)
+                       serializer.serialize(value)
+
+                       # TODO report errors
+                       nsobject = serialized_string.to_s.to_nsstring
+               end
+
+               user_defaults.set_object(nsobject, key.to_nsstring)
+       end
+end
index 5c3aafa..1ea6c8f 100644 (file)
@@ -78,7 +78,7 @@ in "ObjC" `{
 redef class App
        redef fun did_finish_launching_with_options
        do
-               on_create
+               super
                window.native.make_key_and_visible
                return true
        end
index ad44406..9438a0a 100644 (file)
@@ -466,6 +466,8 @@ redef class Text
                end
                return res
        end
+
+       redef fun serialize_to_json(v) do v.stream.write(to_json)
 end
 
 redef class Serializable
@@ -534,10 +536,6 @@ redef class Char
        end
 end
 
-redef class String
-       redef fun serialize_to_json(v) do v.stream.write(to_json)
-end
-
 redef class NativeString
        redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
 end
index 10caaef..cdf1db0 100644 (file)
@@ -221,7 +221,7 @@ redef class Char super DirectSerializable end
 redef class Int super DirectSerializable end
 redef class Float super DirectSerializable end
 redef class NativeString super DirectSerializable end
-redef class String super DirectSerializable end
+redef class Text super DirectSerializable end
 redef class SimpleCollection[E] super Serializable end
 redef class Map[K, V] super Serializable end