         return round(measurement * UIScreen.mainScreen().scale) / UIScreen.mainScreen().scale
     }
     
-    func layoutKeys(model: Keyboard, bounds: CGRect, page pageToLayout: Int) {
+    func layoutKeys(model: Keyboard, bounds: CGRect, page pageToLayout: Int) -> [Key:CGRect]? {
         if bounds.height == 0 || bounds.width == 0 {
-            return
-        }
+            return nil
+        }
+        
+        var keyMap = [Key:CGRect]()
         
         let isLandscape: Bool = {
             let boundsRatio = bounds.width / bounds.height

                return self.rounded(returnWidth)
                }()
            
            for (r, row) in enumerate(page.rows) {
                let rowGapCurrentTotal = (r == page.rows.count - 1 ? rowGapTotal : CGFloat(r) * rowGap)
                let frame = CGRectMake(rounded(sideEdges), rounded(topEdge + (CGFloat(r) * keyHeight) + rowGapCurrentTotal), rounded(bounds.width - CGFloat(2) * sideEdges), rounded(keyHeight))
                
                // basic character row: only typable characters
                if self.characterRowHeuristic(row) {
                    self.layoutCharacterRow(row, modelToView: self.modelToView, keyWidth: letterKeyWidth, gapWidth: keyGap, frame: frame)
                }
                    
                    // character row with side buttons: shift, backspace, etc.
                else if self.doubleSidedRowHeuristic(row) {
                    self.layoutCharacterWithSidesRow(row, frame: frame, isLandscape: isLandscape, keyWidth: letterKeyWidth, keyGap: keyGap)
                }
                    
                    // bottom row with things like space, return, etc.
                else {
                    self.layoutSpecialKeysRow(row, modelToView: self.modelToView, keyWidth: letterKeyWidth, gapWidth: lastRowKeyGap, leftSideRatio: lastRowLeftSideRatio, rightSideRatio: lastRowRightSideRatio, micButtonRatio: self.layoutConstants.micButtonPortraitWidthRatioToOtherSpecialButtons, isLandscape: isLandscape, frame: frame)
                }
            }
        }
    }
    
    func characterRowHeuristic(row: [Key]) -> Bool {

    
    func setAppearanceForOtherKey(key: KeyboardKey, model: Key, darkMode: Bool, solidColorMode: Bool) { /* override this to handle special keys */ }
    
    // TODO: avoid array copies
    // TODO: sizes stored not rounded?
    
    ///////////////////////////
    // KEY POOLING FUNCTIONS //
    ///////////////////////////
    
    func pooledKey(frame: CGRect) -> KeyboardKey? {
        if var keyArray = self.sizeToKeyMap[frame.size] {
            if let key = keyArray.last {
                if keyArray.count == 1 {
                    self.sizeToKeyMap.removeValueForKey(frame.size)
                }
                else {
                    keyArray.removeLast()
                    self.sizeToKeyMap[frame.size] = keyArray
                }
                return key
            }
            else {
                return nil
            }
            
        }
        else {
            return nil
        }
    }
    
    func generateKey() -> KeyboardKey {
        let createKey = { () -> KeyboardKey in
            var keyView = KeyboardKey(vibrancy: nil)
            
            keyView.enabled = true
