Tumgik
#SharedInstance
apps-top · 7 years
Text
Apps-Top - Best iOS Development Podcasts for App Developers
Best iOS Development Podcasts for App Developers
Visit : http://apps-top.com/best-ios-development-podcasts-for-app-developers/
0 notes
joeyrob1 · 4 years
Text
Free login and authentication for iOS apps using phone number
Free login and authentication for iOS apps using phone number
(No Ratings Yet) Loading...
Pham Van Hoang, [email protected], is the author of this article and he contributes to RobustTechHouse Blog
INTRODUCTION
As a developer, you may be familiar with the typical login system using emails, passwords and sign up forms.
However, have you ever found it cumbersome, repetitive and to a certain extent preventive of users from using your app?  Perhaps you have a phone-based dating application and you want to tie user’s account to something that is unique: phone number – an identity that they already use every day without the hassle of dealing (remembering) with passwords. If this idea is so incredible, so why hasn’t anyone started to build a system as such?
But wait, it doesn’t seem that easy at all. To have that feature that I have described, we need to connect to the phone service provider to verify the number and get the confirmation SMS from user. In order to achieve that, we would first need to have SMS APIs to communicate with your app, which in turn connects to a SMS Aggregator system. Next the SMS Aggregator system will need to communicate with service provider in the country where we distribute the app to verify the number and send the confirmation SMS.
With every million verification, it is likely to cost thousands of dollars, not to mention the efforts to ensure reliability of this system as it scales.
On hind sight, that seems rather impossible. Luckily, there is Digits to solve the problem.
SO, WHAT IS DIGITS?
Digits is a brand-new way to log in to apps with just your phone number, which was announced by Twitter at its mobile developer conference in San Francisco, 2015. Moreover it is completely free and more importantly it is secure.
HOW DIGITS WORKS?
“Digits lets people create an account or sign into your app using nothing but their phone number on iOS and Android. Built using the same global, reliable infrastructure Twitter uses, Digits will verify the user’s phone number with a simple customizable user interface that easily integrates into your app.
Digits extends your existing user/profile database. A successful Digits account creation will return a stable userID to attach or match with your user record, and an oAuth token for the device”
Also Digits is available now in 216 countries and in 28 languages.
INTEGRATE DIGITS
Step 1: Register Fabric accounts, Fabric includes Digits and several other tools such as Crashlytics, the crash-reporting tool and MoPub, its advertising platform, and some other useful tools.
Step 2: Download and install Fabric for Xcode
Step 3: Select the project that you want to integrate Digits
  Step 4: Install digits
Step 5: Copy and run the scripts in the instructions. If you don’t know where to run script build phase, click the question mark button. It’ll open up the detailed instructions for you.
Step 6: Follow each step in the instructions and imports Digits module in Appdelegate class
Step 7: Build and run your app to confirm that it’s working as expected
Now, Let’s Work With Some Codes
To initialize DigitsKit with your app’s credentials, pass them to startWithConsumerKey:consumerSecret: before passing the shared instance to Fabric in your app’s AppDelegate.
Calling startWithConsumerKey:consumerSecret: will override any keys which were automatically configured. Automatically configured keys resides in your app’s Info.plist under the key Fabric.
[[Digits sharedInstance] startWithConsumerKey:@"your_key" consumerSecret:@"your_secret"]; [Fabric with:@[[Digits sharedInstance]]]
Using the pre-configured button
// Objective-C - (void)viewDidLoad { DGTAuthenticateButton *digitsButton = [DGTAuthenticateButton buttonWithAuthenticationCompletion:^(DGTSession *session, NSError *error) { // Inspect session/error objects }]; [self.view addSubview:digitsButton]; }
In the view controller that will display the button, instantiate the pre-configured button DGTAuthenticateButton after the view is loaded (e.g. in the viewDidLoad method) and provide a completion block to handle the provided session object:
This will render a button looks like:
    Using your own button
 In the view controller that displays your custom button, capture a tap event as usual and call the authenticateWithCompletion: method with the completion block that handles the session object:
Digits let you customize the buttons and also the confirmation screen very easily.
// Objective-C - (void)didTapButton { [[Digits sharedInstance] authenticateWithCompletion:^(DGTSession *session, NSError *error) { // Inspect session/error objects }]; }
Authentication
Once users logged-in, digits will send the session which includes userID, authToken,authTokenSecret, phoneNumber for you to handle in your app. So simple, huh! There are a lot more awesome features in Digits, you can check them out on the Digits site.
If you have any questions, leave the comments below. Thanks for reading.
  REFERENCE
Twitter Flight – Phone Number Sign In with Digits
https://get.digits.com
Brought to you by the RobustTechHouse team (A top app development company in Singapore).  If you like our articles, please also check out our Facebook page.
Free login and authentication for iOS apps using phone number was originally published on RobustTechHouse - Mobile App Development Singapore
0 notes
arthurknopper · 6 years
Text
Volume View iOS Tutorial
When using audio in your app sometimes you want the user to change the volume. With the help of the MPVolumeView object you create a builtin Volume control which also has the abilty  to redirect the output to an airplay device. In this tutorial we will play a sound and display the volume controls.This tutorial is made with Xcode 9 and built for iOS 11.
Open Xcode and create a new Single View App.
For product name, use IOS11VolumeViewTutorial and then fill out the Organization Name and Organization Identifier with your customary values. Enter Swift as Language and choose Next.
Some audio is needed to play, so download the music file and add it to the project, make sure "Copy items if needed" is selected
Go to the Storyboard and drag two Buttons to the main view. Set the title of the left button to Play. Select the Button and select the Auto Layout pin button. Pin the Button to the top and left and click "Add 2 Constraints".
Set the title of the right button to Stop. Select the Button and select the Auto Layout pin button. Pin the Button to the top and right and click "Add 2 Constraints".
The Storyboard should look like this.
Select the Assistant Editor and make sure the ViewController.swift is visible. Ctrl and drag from the Play Button to the ViewController class and create the following Action
Ctrl and drag from the Stop Button to the ViewController class and create the following Action
The AVFoundation framework is needed for playing the music file and the MediaPlayer framework to use the MPVolumeView. Go to the ViewController.swift file and import the frameworks
import AVFoundation import MediaPlayer
Inside the ViewController class add the following properties
var audioPlayer = AVAudioPlayer() let wrapperView = UIView(frame: CGRect(x: 30, y: 200, width: 300, height: 20))
Implement the viewDidLoad method
override func viewDidLoad() { super.viewDidLoad() // 1 let path = Bundle.main.path(forResource: "Amorphis - My Enemy", ofType: "mp3") let music = NSURL(fileURLWithPath: path!) as URL // 2 do { try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) try AVAudioSession.sharedInstance().setActive(true) try audioPlayer = AVAudioPlayer(contentsOf: music) audioPlayer.prepareToPlay() } catch { print("error") } }
The audio file is extracted from the Application's bundle
The audio session category is set to playback and set to active.
Implement the playSound method
   @IBAction func playSound(_ sender: Any) { // 1 audioPlayer.play() // 2 view.backgroundColor = UIColor.clear view.addSubview(wrapperView) // 3 let volumeView = MPVolumeView(frame: wrapperView.bounds) wrapperView.addSubview(volumeView) }
The audio file starts to play
The wrapper view is added as a subview to the main view
The MPVolumeView is added as a subview of the wrapper view.
Implement the stopSound method.
@IBAction func stopSound(_ sender: Any) { audioPlayer.stop() wrapperView.removeFromSuperview() }
The audio is stopped and the wrapperView including the volume view is removed from the superview.
Build and Run the project, select the play button and change the volume and Output source. Note this can only be run on an actual device, since in the iOS Simulator you will only see a black view.
You can download the source code of the IOS11VolumeViewTutorialat the ioscreator repository on Github.
0 notes
iyarpage · 7 years
Text
Serious Simulation with GameplayKit
The GameplayKit framework is designed to make it easy for game developers to do common tasks like pathfinding, random numbers, basic AI and other cool stuff. This is great — but what if you’re not a game developer?
In this tutorial, you’ll see how these features can not only help test apps that are not games, but also how they can help early-adopters of your app to have more fun. In the process you’ll learn how to use:
Random numbers
The Entity-Component-System architecture
State Machines
…all with GameplayKit!
Getting Started
Download the starter project, then build and run it. You will see the following:
The app lets you share your location with other cyclists and indicate what you’re up to at any given time. If you tap the crosshairs icon the map will center on your simulated location, which is indicated with a bicycle icon. As time passes, the icon moves around showing that you are cycling around.
If you open the status menu you can change your state: Cruisin’ is riding in one direction, Returnin’ rides in the other. Breather shows you are lazing about, sipping espresso. Flat! shows you have a flat tire, and Helpin’ shows you have stopped to lend your pump to someone. The icon will change according to the selected state.
Note: You might recognize the “City Bicycle Ride” route from the iOS simulator. For convenience this app uses values captured from that route. If the simulator was providing values in real time, it would be much harder to stop any of the users when they rest. If you are interested in exploring how that is done, have a look at SimLocationManager.swift – but that’s outside the scope of this tutorial.
The only view controller in this app is MapViewController, with a simple MKMapView. It watches out for EntityStatusNotification notifications, and when one is received, updates the annotation accordingly.
The User object updates itself whenever it receives a notification from the TimeUpdateNotifier. When an update is received the next position is read and an EntityStatusNotification is posted. And that’s it — a very simple map app.
It’s a great app idea, but early adopters might not be interested unless there’s a lot of other users to interact with. You would probably never attract enough users to get your great idea off the ground. Worse, you might never uncover bugs that may arise from interaction between users until you tried to go live. But you can fix this all with GameplayKit!
A Bit of GameplayKit Background
Apple introduced GameplayKit at WWDC 2015, and enhanced it further in 2016. In this tutorial, you’ll use GameplayKit to create artificial users, called “simulants”.
To the first few users in a multi-user app, it’s a lonely place — you must build critical mass. Interactions between users can reveal unexpected bugs. By creating artificial users you can test more deeply.
Computer games have “Non-Player Characters” (or NPCs) that interact with the player in a way that makes the game interesting. But there’s no reason NPCs can’t interact with your app just like a real user — all you need to do is connect them to the same API.
This tutorial will show how to use the GKRandom class, the Entity-Component-System implementation and GKStateMachine. Watching Apple’s video from WWDC 2015, Introducing GameplayKit is highly recommended because there is a lot more to discover including agents, behaviours, pathfinding and more.
Ray’s earlier tutorial GameplayKit Tutorial: Entity-Component System, Agents, Goals, and Behaviors goes into this architectural pattern in some depth and working through that tutorial is highly recommended.
So Lonely…
Okay, back to the task at hand.
You know that MKMapView can display many annotations simultaneously, so it should be a simple matter to have more users on the map. Time for you to create some simulants!
The first thing to do is allow the map to differentiate between the user and the simulants. In MapViewController.swift, add another two keys to the EntityStatusKey enum, called identity and displayName:
enum EntityStatusKey { case identity case displayName case state case coordinate }
Next, you need to identify the user with these two new keys. In User.swift, add the following with the other properties:
let kUserDisplayName = "You" let uniqueIdentifier = NSUUID().uuidString
This creates constants for the readable name and a unique identifier.
Now change the first line of postStatusUpdate() as follows:
let statusInfo: [EntityStatusKey: Any] = [.identity: uniqueIdentifier, .displayName: kUserDisplayName, .state: currentState, .coordinate: coordinate as Any]
This includes the new values in the notification posted in response to the timer notification.
Next, you need to add a couple of components: one to represent the identity of a stimulant, the other to handle location updates as part of the location system.
Create a new file called IdentityComponent.swift and replace its contents with the following:
import UIKit import GameplayKit // This component describes who this entity is class IdentityComponent: GKComponent { let uniqueIdentifier = NSUUID().uuidString let displayName: String var greetingDone = false required init(name: String) { self.displayName = name super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
This is a GKComponent subclass that contains two pieces of identifying information:
A unique identifier for the entity.
A display name — something friendly and readable.
Next, create a new file called LocationComponent.swift containing the following:
import Foundation import MapKit import GameplayKit class LocationComponent: GKComponent { var locationIndex: Int var countingUp: Bool var coordinate: CLLocationCoordinate2D? required override init() { // 1 let locationCount = SimLocationManager.sharedInstance.locationCount() locationIndex = Int(arc4random_uniform(UInt32(locationCount))) if locationIndex % 2 == 0 { countingUp = true } else { countingUp = false } super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func update(deltaTime seconds: TimeInterval) { // 2 guard let identityComponent = entity?.component(ofType: IdentityComponent.self) else { return } locationIndex = SimLocationManager.sharedInstance.nextLocationIndex( countingUp: countingUp, fromIndex: locationIndex) coordinate = SimLocationManager.sharedInstance .coordinateForIndex(index: locationIndex) // 3 let statusInfo: [EntityStatusKey: Any] = [.identity: identityComponent.uniqueIdentifier, .displayName: identityComponent.displayName, .state: EntityState.cruisin, .coordinate: coordinate as Any ] NotificationCenter.default.post(name: .EntityStatusNotification, object: self, userInfo: statusInfo) } }
This is another GKComponent; the key points are:
Just like the User object, you initialize this object by randomly picking a direction and start location.
For each update, you access the IdentityComponent belonging to this entity…
…to post the uniqueIdentifier and displayName along with the changed location to the EntityStatusNotification notification.
Note: Components in the same entity may access one another. This may seem like it breaks some Object-Oriented rules, but this is not OO programming!
Controlling Your Simulants
Something will have to manage your simulants and cause them to update regularly. Create a new file called SimulationEngine.swift and replace the contents with the following:
import GameplayKit extension Notification.Name { static let SimulantChangeNotification = Notification.Name(rawValue:"SimulantChangeNotification") } enum ComponentKey { case identity } // 1 let simList: [[ComponentKey: String]] = [ [.identity: "Waldo"], [.identity: "Sue"], [.identity: "Vikas"] ] class SimulationEngine { static let sharedInstance = SimulationEngine() var entities: [String: GKEntity] = [:] var componentSystems: [GKComponentSystem<GKComponent>] required init() { let locationSystem = GKComponentSystem(componentClass: LocationComponent.self) componentSystems = [locationSystem] createSimulants() } deinit { NotificationCenter.default.removeObserver(self) } private func createSimulants() { for simulantDetail in simList { let simulant = GKEntity() // 2 simulant.addComponent(IdentityComponent(name: simulantDetail[.identity]!)) simulant.addComponent(LocationComponent()) add(entity: simulant) } } private func add(entity: GKEntity) { // 3 let uniqueKey = (entity.component(ofType: IdentityComponent.self)?.uniqueIdentifier)! entities[uniqueKey] = entity // 4 for componentSystem in componentSystems { componentSystem.addComponent(foundIn: entity) } } // 5 @objc func timerUpdateNotification() { for componentSystem in componentSystems { componentSystem.update(deltaTime: 0) } } } // Public interface for the SimEngine class. extension SimulationEngine { func start() { // 6 NotificationCenter.default.addObserver(self, selector: #selector(timerUpdateNotification), name: .TimeUpdateNotification, object: nil) } func stop() { NotificationCenter.default.removeObserver(self) } }
Here’s what’s going on in the code above:
This array of dictionaries defines data that will create each simulant.
Each simulant has components added during creation.
You use the entity’s unique key to store it in the array.
Add the entity to each all of the component systems
Each timer update causes SimulationEngine to update all of the component systems.
Register this object to receive timer notifications.
Now head back to MapViewController.swift and add the following to the end of init(coder:):
// release the simulants! SimulationEngine.sharedInstance.start()
And then change the definition of userInformation to make it contain a dictionary:
var userInformation: [String: (MKPointAnnotation, EntityState)] = [:]
The notification handler now needs to be able to deal with updates from the user OR one of the simulants. To do this, replace the contents of entityStatusUpdateNotification(notification:) with the following:
// 1 let statusDictionary = notification.userInfo! let coordinate = statusDictionary[EntityStatusKey.coordinate] as! CLLocationCoordinate2D let state = statusDictionary[EntityStatusKey.state] as! EntityState let uniqueIdentifier = statusDictionary[EntityStatusKey.identity] as! String let displayName = statusDictionary[EntityStatusKey.displayName] as! String // 2 var userInfo = userInformation[uniqueIdentifier] if let existingInfo = userInfo { mapView.removeAnnotation(existingInfo.0) } else { userInfo = (MKPointAnnotation(), state) } // 3 userInfo!.1 = state userInfo!.0.title = displayName userInfo!.0.coordinate = coordinate userInfo!.0.subtitle = uniqueIdentifier // 4 userInformation[uniqueIdentifier] = userInfo! // 5 mapView.addAnnotation(userInfo!.0)
Going through this step-by-step:
Get the details from the notification dictionary, including the new identity and displayName values you just added.
If the annotation exists, remove it; otherwise create one.
Set up the annotation.
Store the annotation for future updates.
Put it on the map.
Finally, now that userInformation is a dictionary, you need to modify the guard statement in mapView(_:viewFor:) as follows:
guard let uniqueIdentifier = annotation.subtitle, let userInfo = userInformation[uniqueIdentifier!] else { // it should never happen that a view for an unknown annotation type is requested assert(false) }
Build and run to see your simulants in action!
Can you tell which icon is the user?
In brief, the app is now working like this:
TimeUpdateNotifier sends a notification that the timer has fired.
User gets the notification, gets a new location from SimLocationManager and builds a status update dictionary using the fixed name for the user (“You”) and a UUID.
MapViewController receives the user notification and updates the user’s map annotation.
SimulationEngine gets the timer fired notification and processes the component systems in every entity.
The Location component of each entity gets a new location and together with identity information, builds a status update dictionary and posts it as a notification.
MapViewController receives the notification, and updates the simulant’s annotation.
Congratulations! That’s your first Entity Component System implemented.
Now multiple users are riding around. Or perhaps zombies would be a better name, all they do is move aimlessly in one direction. It would be nice if they could decide to rest, or to change direction. In short, they need to change state.
States
It’s time for GKStateMachine and some GKStates. State machines are a big topic! Briefly, they allow you to check an object’s state, set triggers for transition to another state and set rules about what states can be transitioned to (i.e., you can’t get to there from here). States are important because they allow you to create predictable patterns of behavior, Pac-Man being the classic example.
Cruisin’ State
The basic state is Cruisin’, so create a file called CruisinState.swift and add the following code:
import GameplayKit class CruisinState: GKState { let entity: GKEntity let extraTime: TimeInterval var timeInState: TimeInterval = 0 init(forEntity: GKEntity) { self.entity = forEntity // 1 self.extraTime = TimeInterval(GKRandomDistribution.d6().nextInt()) super.init() } // 2 override func isValidNextState(_ stateClass: AnyClass) -> Bool { if (stateClass == ReturninState.self) { return true } return false } // 3 override func willExit(to nextState: GKState) { if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is leaving CruisinState") } } // 4 override func didEnter(from previousState: GKState?) { timeInState = 0 if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is entering CruisinState") } } // 5 override func update(deltaTime seconds: TimeInterval) { timeInState += 1 if let identity = entity.component(ofType: IdentityComponent.self) { if timeInState > extraTime { stateMachine?.enter(ReturninState.self) } } } }
Going through this step-by-step:
GKRandomDistribution is used to choose a time interval (see note below).
If a state is a valid next state, return true, otherwise false. Here, ReturninState is valid — you’ll define that in a moment.
Gives the state a chance to do something before it exits. Here, it prints a message.
Gives the state a chance to do something as it is entered. Here, it prints a message.
The state updates — it may check for transition to another state or perform other actions on every update.
Note: Before GameplayKit, we all used arc4random_uniform. GKRandomDistribution is much easier and more flexible! The d6 function name may look strange, but among die-rollers (board gamers and role playing gamers) that’s a way to specify the roll of a random number. The number after the ‘d’ is the number of sides the die has. If there’s a number before the ‘d’, that’s a multiplier.
Here d6() is used to roll a number between 1 and 6 inclusive. d6 and d20 are pre-defined dies, but you can create one with any number of sides. Or you might want a Gaussian distribution (i.e. 2d6 or even 3d4 rather than 1d12) in your dice rolls. Need to shuffle an array containing card objects? GKRandomDistribution does it all.
In the states this random function is used to generate a value called extraTime. This random value represents how much the entity “likes” this particular state. You would have noticed that each simulant has an inertia value that is valid over all states. The extraTime value is added to the inertia value for more variability. If a simulant remained in every state for the same amount of time patterns would soon appear, spoiling the appearance of individuality.
Returnin’ State
The Returnin’ state is the opposite of Cruisin’, but its definition is exactly the same. Create a new file called ReturninState.swift and insert the same code above, but replace all mentions of CruisinState with ReturninState and vice versa. They are simply opposites of each other.
State Component
Giving an entity a state is as simple as giving it a state machine component.
Create a new file called StateComponent.swift and add the following code:
import GameplayKit class StateComponent: GKComponent { // 1 let cruisinState: CruisinState let returninState: ReturninState let stateMachine: GKStateMachine required init(forEntity: GKEntity) { cruisinState = CruisinState(forEntity: forEntity) returninState = ReturninState(forEntity: forEntity) // 2 stateMachine = GKStateMachine(states: [cruisinState, returninState]) stateMachine.enter(CruisinState.self) super.init() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func update(deltaTime seconds: TimeInterval) { // 3 stateMachine.currentState?.update(deltaTime: seconds) } // 4 func currentState() -> EntityState { var reportedState: EntityState = .cruisin if stateMachine.currentState! == cruisinState { reportedState = .cruisin } else if stateMachine.currentState! == returninState { reportedState = .returnin } return reportedState } }
Taking each numbered comment in turn:
A state component has one of each state it can be in.
The GKStateMachine is initialized with an array of possible states and is given a start state.
The current state has its update method called whenever this component does. This is an example of the Strategy Pattern.
This component can report its state, in terms of the EntityState enum, for convenience.
Simulation Engine
You’ll require a few changes in SimulationEngine.swift before you can add your new state machine component to the Simulants.
First, the initialization of componentSystems in init() should be changed as follows:
let stateSystem = GKComponentSystem(componentClass: StateComponent.self) componentSystems = [locationSystem, stateSystem]
This adds the state machine to the array of component systems.
Next, in createSimulants(), add the following before the call to add(entity:):
simulant.addComponent(StateComponent(forEntity: simulant))
This adds StateComponent to each simulant.
Identity Component
To make things more realistic, your Simulants are going to be given a human trait: resistance to change. This is simply how much they like to stay in a state before changing.
In IdentityComponent.swift, add the following property:
let inertia: TimeInterval
Now set it in init() by adding the following, just before you call super.init():
let random = GKRandomSource() let dice3d6 = GKGaussianDistribution( randomSource: random, lowestValue: 3, highestValue: 18) self.inertia = TimeInterval(dice3d6.nextInt())
Head back to both CruisinState.swift and ReturninState.swift and change the second if statements in update(deltaTime:) to use your new inertia variable:
if timeInState > identity.inertia + extraTime {
Don’t forget to change this in both files!
The result is that your simulants will persist in any state they are in for around 3d6 + 1d6. Time has been shortened here for simulation purposes. You can make it more realistic when you have an entire day to sit and watch!
Note: Doesn’t IdentityComponent really describe three aspects of the entity? Why not have an “InertiaComponent”, a “DisplayNameComponent” and a “UniqueIDComponent”? No reason other than these would all be very tiny, static components, which is a lot of overhead, but that would be a perfectly valid way to do it.
Which Way?
The simulant’s direction should be shown on the map, so they have to report their current state.
In LocationComponent.swift, change the contents of update(deltaTime:) to:
// 1 guard let identityComponent = entity?.component(ofType: IdentityComponent.self), let currentState = entity?.component(ofType: StateComponent.self)?.currentState() else { return } // 2 var direction = countingUp if currentState == .returnin { direction = !countingUp } locationIndex = SimLocationManager.sharedInstance.nextLocationIndex( countingUp: direction, fromIndex: locationIndex) coordinate = SimLocationManager.sharedInstance.coordinateForIndex(index: locationIndex) // 3 let statusInfo: [EntityStatusKey: Any] = [.identity: identityComponent.uniqueIdentifier, .displayName: identityComponent.displayName, .state: currentState, .coordinate: coordinate as Any] NotificationCenter.default.post(name: .EntityStatusNotification, object: self, userInfo: statusInfo)
Here’s the play-by-play:
Get the currentState from the entity.
If state is Returnin’ travel direction is reversed, so the direction of the next location would be NOT countingUp.
Rather than always posting Cruisin’, you now post currentState.
Multi-Directional Simulants!
Build and run your app.
“I like this one. One simulant goes one way and the other goes the other.”
This doesn’t look so different… but eventually the simulants will change direction! State machine success! Picture your zombies riding into imaginary walls and turning around. A bit smarter — but not by much.
Tired Simulants? Breathers, and Flats.
OK, but now those simulants can get tired. It’s time to give them the ability to take a coffee break. And while it’s not quite as much fun as unleashing Godzilla on a simulated city, you’re going to give them flat tires too! Both situations are states.
Breather State
Create a new file called BreatherState.swift and replace the contents with the following:
import GameplayKit class BreatherState: GKState { let entity: GKEntity let extraTime: TimeInterval var timeInState: TimeInterval = 0 init(forEntity: GKEntity) { self.entity = forEntity self.extraTime = TimeInterval(GKRandomDistribution.d6().nextInt()) super.init() } override func isValidNextState(_ stateClass: AnyClass) -> Bool { // 1 if (stateClass == CruisinState.self || stateClass == ReturninState.self) { return true } return false } override func willExit(to nextState: GKState) { if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is leaving BreatherState") } } override func didEnter(from previousState: GKState?) { timeInState = 0 if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is entering BreatherState") } } override func update(deltaTime seconds: TimeInterval) { timeInState += 1 guard let identity = entity.component(ofType: IdentityComponent.self) else { return } if timeInState > (identity.inertia + extraTime) { // 2 if (GKRandomDistribution.d6().nextInt() % 2 == 0) { stateMachine?.enter(CruisinState.self) } else { stateMachine?.enter(ReturninState.self) } } } }
The new state is very similar to the previous states. The differences are:
Both Cruising’ and Returnin’ are valid when leaving BreatherState. Either way is fine.
While resting, the simulant can randomly choose a direction to travel in when they get moving.
Flat State
Now for a flat tire. Create a new file called FlatState.swift, with the following contents:
import GameplayKit class FlatState: GKState { let entity: GKEntity var fixedBy: GKEntity? var previousState: GKState.Type? init(forEntity: GKEntity) { self.entity = forEntity super.init() } override func isValidNextState(_ stateClass: AnyClass) -> Bool { if (stateClass == CruisinState.self || stateClass == ReturninState.self) { return true } return false } override func willExit(to nextState: GKState) { if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is leaving FlatState.") } } override func didEnter(from previousState: GKState?) { // 1 fixedBy = nil // 2 self.previousState = type(of: previousState!) if let identity = entity.component(ofType: IdentityComponent.self) { print("\(identity.displayName) is entering FlatState - Oh no!") } } override func update(deltaTime seconds: TimeInterval) { // 3 let theOdds = 100 let oddsDie = GKRandomDistribution(forDieWithSideCount: theOdds) if (oddsDie.nextInt() == theOdds) { print("Rolled a \(theOdds)") fixedBy = self.entity } if fixedBy != nil { // 4 stateMachine?.enter(previousState.self!) } } }
Here’s what’s happening:
On entering FlatState, the flat tire has not been fixed.
The previous state is recorded so it can be resumed when the flat is fixed.
Simulants aren’t very good at fixing flats — they have a 1 in 100 chance of fixing it per update.
Resume travel in the previous state’s direction.
Stop Moving!
What’s going to happen if you show simulants with flat tires or hot coffee continuing to move around on the map? You’re on the verge of encouraging dangerous behavior in real users — and you don’t want to get sued!
In LocationComponent.swift, make this small change in update(deltaTime:), wrapping the existing line in an if statement:
// update location from locations if the simulant is in a moving state if currentState == .cruisin || currentState == .returnin { locationIndex = SimLocationManager.sharedInstance.nextLocationIndex( countingUp: direction, fromIndex: locationIndex) }
This only updates the location if in a moving state: Cruisin’ or Returnin’.
Transitioning
Now that you have two new states, you need to let them be valid next states when the simulants are moving.
Update the if statement in isValidNextState(_:) in both CrusinState.swift and ReturninState.swift to the following tests:
if stateClass == BreatherState.self || stateClass == FlatState.self { return true }
This allows BreatherState and FlatState to be valid next states.
You also need to create the possibility of having a flat tire. Add the following to the top of update(deltaTime:) in both files too:
// rolling 1 on a d20 means a flat! if GKRandomDistribution.d20().nextInt() == 1 { stateMachine?.enter(FlatState.self) return }
Also, to give your Simulants a chance of a breather, replace the stateMachine?.enter(...) line in update(deltaTime:) in both files with:
stateMachine?.enter(BreatherState.self)
This just means that when the Simulant’s inertia and extra time are used up, they’ll take a breather!
Add Those States
To make these new states possible, add the following constants to the top of the StateComponent class:
let breatherState: BreatherState let flatState: FlatState
You need to now initialize those constants. Add the following to init(forEntity:), just under the other state initializations:
breatherState = BreatherState(forEntity: forEntity) flatState = FlatState(forEntity: forEntity)
This initializes the constants. Now change the initialization of stateMachine to include them:
stateMachine = GKStateMachine(states: [cruisinState, returninState, breatherState, flatState])
This simply includes your new states.
Finally, in currentState(), add the following to the end of the existing if statement:
else if stateMachine.currentState! == breatherState { reportedState = .breather } else if stateMachine.currentState! == flatState { reportedState = .flat }
This returns the correct state.
Build and run. You’ll see your simulants entering new states — stopping for a coffee (Breather) or for a flat tire (Flat) — almost like real people. All they need is Lycra!
Flats occur surprisingly often! (Usually right after you tell someone how long it has been since you had one.)
User to the Rescue!
If you watch the app for a while you’ll notice that simulants spend a lot of time in the Flat state. It’s time to be kind and help them!
In User.swift, add the following to setNewState(newState:), inside the if statement, but before the call to postStatusUpdate():
if currentState == .helpin { NotificationCenter.default.post(name: .HelpArrivedNotification, object: self, userInfo: nil) }
This simply posts a notification if the user is in the Helpin’ state. You’ll define the notification shortly.
To see if the user can fix a flat tire, the simulation engine will respond to the notification and check to see if any of the stimulants actually have a flat. If one does, and they’re within 100m of the user, the tire will be fixed.
Open SimulationEngine.swift and add the following to the top of the file:
import MapKit
SimulationEngine is going to do some distance calculations, so this import becomes necessary. Next, add the following inside the Notification.Name extension:
static let HelpArrivedNotification = Notification.Name(rawValue:"HelpArrivedNotification")
This is the name of the notification you posted in the User class, for when the User is helping.
Next, add the following to the end of start():
// register for help notification NotificationCenter.default.addObserver(self, selector: #selector(helpArrivedNotification), name: .HelpArrivedNotification, object: nil)
This just registers for the new notification message.
Now, time to add the method called when the notification is received. Add this inside the main class definition:
// help has arrived - see if any simulants are close enough and have a flat @objc func helpArrivedNotification(notification: Notification) { let maximumHelpDistance = 100.0 // meters for (_, entity) in entities { guard let entityState = entity.component(ofType: StateComponent.self), entityState.currentState() == .flat, let entityLocationComponent = entity.component(ofType: LocationComponent.self), let entityCoordinate = entityLocationComponent.coordinate, let helper = notification.object as? User, let helperCoordinate = helper.coordinate else { continue } let entityLocation = CLLocation(latitude: entityCoordinate.latitude, longitude: entityCoordinate.longitude) let helperLocation = CLLocation(latitude: helperCoordinate.latitude, longitude: helperCoordinate.longitude) let helperDistance = entityLocation.distance(from: helperLocation) if helperDistance < maximumHelpDistance { entityState.flatState.fixedByUser = true } } }
The guard statement checks that there are entities in the Flat state and that valid locations exist for the entity and the user that posted the notification. If the user is close enough, you set a Boolean to indicate that the flat was fixed by the user. You'll add this Boolean next.
Open FlatState.swift and add the Boolean as a property:
var fixedByUser = false
This is set by the simulation engine code you added above.
You'll need to reset this variable every time you enter the Flat state, so add the following to the beginning of didEnter(from:):
fixedByUser = false
Finally, in update(deltaTime:), change the last if statement, to check the variable's value:
if fixedBy != nil || fixedByUser {
Build and run. Make yourself feel good by fixing some flats, using your Helpin' state - but remember that you have to be within 100m to help!
Helping is Magical.
Where To Go From Here?
You can download the final version of the code here.
You have created artificial users that can help test and populate an application. There’s a few directions you could take this in:
The console log gets busy - try CocoaLumberjack, SwiftyBeaver or NSLogger to make it easier to read.
Add more simulants.
Make your simulants kind, so they fix each other's flats.
This could even be the basis for a game!
For further reading, check out the following:
Entity Systems are the future of MMOG development
Wikipedia article Entity–component–system
Understanding Component-Entity-Systems
Introduction to Component Based Architecture in Games
Evolve your Hierarchy
I hope you enjoyed this tutorial, and if you have any questions or comments, or more resources to share, please join the forum discussion below!
The post Serious Simulation with GameplayKit appeared first on Ray Wenderlich.
Serious Simulation with GameplayKit published first on http://ift.tt/2fA8nUr
1 note · View note
iosdevnotes · 9 years
Text
Singleton in Swift
import Foundation
let sharedInstance = AppSingleton();
final class AppSingleton{       
var aSharedVariable:NSMutableString!       
private init(){}
}
0 notes
g8production · 11 years
Text
iOS: The singleton pattern
Tumblr media
The Singleton Pattern is one of must used patterns in Objective-C. 
If you are a Objective-C developer, of course you have seen the singleton pattern.
[UIApplication sharedApplication]
[NSBundle mainBundle]
[NSUserDefaults standardUserDefaults]
Do you know them?
they are class methods (they starts with "+"); they will return the (unique) shared instance of an object; they are singletons!
That's all!
Well, below is explained how to create a singleton for your custom class.
First sign your .h file with a class method +(id)sharedInstance;. It must return a object (id is ok!).
#import <Foundation/Foundation.h> @interface MyCustomClass : NSObject +(id)sharedInstance; @end
Second implement the +(id)sharedInstance method into the .m file. It will create the (shared) instance only one time and always return it.
+ (id)sharedInstance { static MyCustomClass *theSharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ theSharedInstance = [[self alloc] init]; }); return theSharedInstance; }
That's really all!
Don't forget: the singleton is also a whisky. We love singleton!
0 notes