Website Links

Friday, 8 May 2015

Core Data for Swift

Having core data enabled for an xCode project allows you to persist data between user sessions. There is an option you should tick when creating the project, and that is to use core data. However, if you have already created your project and made significant process without turning on this option, you can just create a new project and copy across the relevant code from AppDelegate.swift!

Defining Entities

xCode makes it excessively easy to define entities. First, you will need to open the .xcdatamodeld file and click "Add Entity", your new entity will appear under the list of entities and you can double click on it to rename it. Adding attributes can be done by pressing the "+" in the attributes section. Attributes need to start with a lowercase name, and some names are reserved such as "isDeleted". You can then select what data type that attribute will be stored as. Your finished entity should look something like this:



There is also a view which allows you to edit entities and add relationships in a UML style format. If you would prefer that change the "Editor Style" which is located in the bottom right.

Defining Relationships

You are also able to define relationships between entities. Once a relationship is created, you can view its details and set properties such as how deletes are handled. For example, when a scenario is deleted I want all stones linked to it to also be deleted, so I set the "Delete Rule" to cascade. Once you have finished defining your relationships you should should have something that looks like this:



The above diagram shows that a Stone entity has one Scenario entity, and a Scenario entity has many Stone entities.

Creating Entities


  func createScenario() {
    let appDelegate = UIApplication.sharedApplication().delegate 
         as! AppDelegate
    let context = appDelegate.managedObjectContext!
        
    //Getting entity description for entity named "Scenario"
    //and creating object to save from it     
    let scenarioEntityDescription =  NSEntityDescription
         .entityForName("Scenario", inManagedObjectContext: context)!
    var scenario =  NSManagedObject
                     (entity: scenarioEntityDescription,
                      insertIntoManagedObjectContext: context)
        
    //Setting entity's attributes.  
    scenario.setValue(nameTextField.text, forKey: "name")
    scenario.setValue(descriptionTextField.text, forKey: "scenario")
    scenario.setValue(tagTextField.text, forKey: "tag")

    //Adding stone for stones relationship.
    //Excluded setting of the "Stone" entity's attributes 
    //for brevity.
    //First step is to get a set for the "stones" 
    //relationship from the scenario.
    var stonesSet = scenario.mutableSetValueForKey("stones")
    let stoneEntityDescription = NSEntityDescription
         .entityForName("Stone", inManagedObjectContext: context)!
    var stoneEntity = NSManagedObject
                       (entity: stoneEntityDescription,
                        insertIntoManagedObjectContext: context)
    stonesSet.addObject(stoneEntity)

    //Setting the stones relationship for the scenario.
    //Saving, please note error handling was excluded.
    scenario.setValue(stonesSet, forKey: "stones")
    context.save(nil)
  }


Fetching Entities


  //Returns an NSArray of all "Scenario" entities.
  //If you want to modify this array (add/remove) then you will need
  //a NSMutableArray using scenarios.mutableCopy() as! NSMutableArray.
  func fetchScenarios() -> NSArray {
    let appDelegate = UIApplication.sharedApplication().delegate 
         as! AppDelegate
    let context = appDelegate.managedObjectContext!
        
    //Setting up request for entity named "Scenario"
    //You can also specify search constraints.     
    var request = NSFetchRequest(entityName: "Scenario")
    request.returnsObjectsAsFaults = false
        
    //Fetching all "Scenario" entities.
    //It will also load all "Stone" entities linked
    //with a "Scenario" entity by "stones" relationship.
    return context.executeFetchRequest(request, error: nil)!
  }


Updating Entities


  //Update the "tag" attribute of a given "Scenario" entity.
  func changeScenarioTag(scenario: NSManagedObject, newTag: String) {
    let appDelegate = UIApplication.sharedApplication().delegate 
         as! AppDelegate
    let context = appDelegate.managedObjectContext!
    
    //Making change to the "Scenario" entity's "tag" attribute.
    //Committing the change.
    scenario.setValue(newTag, forKey: "tag")
    context.save(nil)
  }


Deleting Entities


  func deleteScenario(scenario: NSManagedObject) {
    let appDelegate = UIApplication.sharedApplication().delegate 
         as! AppDelegate
    let context = appDelegate.managedObjectContext!
    
    //Telling context we want to delete the scenario object.
    //As we specified a cascade delete in our relationship "stones"
    //the "Stone" entities linked with the "Scenario will be deleted also.
    context.deleteObject(scenario)

    //Commiting delete.
    context.save(nil)
  }


Tuesday, 5 May 2015

Swift Constructors

If you are interested in creating your own subclass of one of Swift's built in objects, you may often want to pass in additional arguments to the constructor. However, it isn't immediately obvious how to do this. The following code shows you how:

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    convenience init(color: UIColor, bounds: CGRect) {
        self.init(frame: bounds)
        self.color = color
    }


    // will give you an error if you try to compile without this
    // after adding the above constructors
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }



To explain this a bit further, when you implement a custom constructor you will get an error if you don't implement the required constructor. The constructor you define must be prefixed with convenience and should make a call to the parent class constructor. However, you cannot make the call to the parent class constructor directly from your convenience constructor. The first constructor overrides and then calls the parent constructor so that you can make a call to it.

Friday, 1 May 2015

Preventing Backwards Navigation from a Bar Button Item in Swift

Hello there, sometimes you wish to return to the root view controller when you click on a bar button item in the navigation bar of an app with a navigation controller. For example, you may save something that you don't want the user to be able to go back to. Fortunately, there is away to do this and it is by using a segue with an identifier.
  1. Create a segue from your Bar Button Item
  2. Click on your segue and give it an identifier
  3. Implement the following function in the view controller which has the bar button you added a segue for:

    override func shouldPerformSegueWithIdentifier(identifier: 
        String?, sender: AnyObject?) -> Bool {
        
        if (identifier == "save") {
            saveBoard()
        }
        
        self.navigationController?
            .popToRootViewControllerAnimated(true)
        
        return false
    }


The above handles 2 different segues in my project, one is cancel in which case I want to return to the root view controller without having the ability to go back to my board that I decided I no longer wanted to save. The other is save, when my saving segue is called I want to call my function which saves the board and then goes back to the root view controller. The boolean returned by the function specifies whether the segue was cancelled or not... I returned false as I wanted to handle the navigation back to the root view controller by myself in all cases. The function shouldPerformSegueWithIdentifier could also be useful to implement if you wanted to validate the data a user entered before navigation.

iAd Advertisements Not Showing

iAd advertisements can take a while to show up even if your app has the status Live Ads and has been processed for the App Store. If your app has only been approved in the past week I wouldn't worry about whether your iAd integration worked or not (unless you have your fill rate set to 100% in the simulator and cannot see ads)! Here is a list of how long ads have taken to show up in our apps:

App Ad Type Time to show
iou free Banner ads 7 days
Angle Reader free Interstitial ads 3 days

Thursday, 16 April 2015

Integrating with iAd

This is pretty simple... Firstly go to iTunes and then to the Agreements, Tax and Banking section. Select and agree to the iAd App Network contract. Once you have done that, go ahead and open your Xcode project. Go to your project, and in the General section go down to Linked Frameworks and Libraries, hit the '+'and find iAd.framework. The iAd framework comes installed so you don't even need to go and download it first!

iAd BannerView

Go to your view controller where you want to display ads, import iAd and set self.canDisplayBannerAds to true. This will automatically handle the adding of a banner view to your view and resize your view depending on the availability of ads. It will also hide the banner view when no ads can be fetched.

iAd Interstitials

This is a little bit more complicated than banner views but works well when you don't want to make space for banner views, and are commonly used in games. Once again, go to the view controller you want to have show interstitial views and import iAd. You will also need to make it implement ADInterstitialAdDelegate, and add the following code:


    var interstitialAd : ADInterstitialAd!
    var interstitialAdView : UIView!
    var closeButton : UIButton!
    
    override func viewDidLoad() 
    {
        super.viewDidLoad()

        // any viewDidLoad logic you have
        
        closeButton = UIButton.buttonWithType(UIButtonType.System)
                      as! UIButton
        closeButton.frame = CGRectMake(self.view.frame.width - 42,
                                       10, 32, 32)
        closeButton.layer.cornerRadius = 16

        // Note: I have an image asset named "Close"
        // You will need to add one for this to work
        var closeImage = UIImage(named: "Close")
        closeButton.backgroundColor = UIColor.whiteColor()
        closeButton.tintColor = UIColor.blackColor()
        closeButton.setImage(closeImage, 
                             forState: UIControlState.Normal)
        closeButton.addTarget(self, action: "closeAd:", 
                              forControlEvents: .TouchDown)
    }
    
    func closeAd(sender: UIButton) 
    {
        closeButton.removeFromSuperview()
        if ((interstitialAdView) != nil) {
            interstitialAdView.removeFromSuperview()
        }
    }
    
    // call whenever you want to show an ad
    func showAd() 
    {
        interstitialAd = ADInterstitialAd()
        interstitialAd.delegate = self
    }
    
    func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) 
    {
        interstitialAdView = UIView()
        interstitialAdView.frame = self.view.bounds
        self.view.addSubview(interstitialAdView)
        
        interstitialAd.presentInView(interstitialAdView)
        UIViewController.prepareInterstitialAds()
        
        interstitialAdView.addSubview(closeButton)
    }
    
    func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!)
    {
        closeAd(closeButton)
    }
    
    // can do things with the error if you want to... I chose not to
    func interstitialAd(interstitialAd: ADInterstitialAd!,
                        didFailWithError error: NSError!) 
    {
        closeAd(closeButton)
    }




Alternatively, it may be worthwhile implementing this in another view controller that all your view controllers that show interstitial ads will extend.

Friday, 20 February 2015

Euler Angles

Euler angles allow you to specify any 3D rotation with just 3 values, these values are as follows:

Pitch

Up and down (i.e. looking up and down)

Yaw

Side to side (i.e. looking left and right)

Roll

Tilting (i.e. tilting your head from side to side)

References

Math for Game Developers - Mouse Control (Euler Angles) Video on YouTube

Wednesday, 18 February 2015

App Icons and iTunes Icons for iOS Apps

You can change what App Icon your iOS App is going to have through the assets catalog. You will need to create quite a few different sizes and I've found the best way to do this is to make the biggest one and resize it accordingly, but where do all these different sizes go? First you will need to enable all the sizes you need, by clicking on the AppIcon asset and finding the following pane:



You can enable/disable these as appropriate for your app, and you should end up with an asset catalog that looks like this:



The above image should tell you what image sizes you will need for your App icons, and where they need to be placed in the catalog.

References

Apple Developer